This technical note describes why text doesn’t always look the way you expect depending on the environment you are in.
There are a number of Macintosh text editing applications where layout is critical. Unfortunately, text on a newer machine sometimes prints differently than text on a 64K ROM Macintosh. Let’s examine some differences you should expect and why.
The differences we will consider here are only differences in the layout of text lines (line layout), not differences in the appearance of fonts or the differences between different printers. Differences in line layout may affect the position of line, paragraph and page breaks. The four variables that can affect line layout are fonts, the printer driver, the font manager mode, and ROMs.
Fonts
Every font on a Macintosh contains its own table of widths which tells QuickDraw how wide characters are on the screen. For every style point size there is a separate table which may contain widths that vary from face to face and from point size to point size. Character widths can vary between point sizes of characters even in the same face. In other words, fonts on the screen are not necessarily linearly scalable.
Non-linearity is not normally a problem since most fonts are designed to be as close to linear as possible. A font face in 6 point has very nearly the same scaled widths of the same font face in 24 point (plus or minus round-off or truncation differences). QuickDraw, however, requires only one face of any particular font to be in the System file to use it in any point size. If only a 10 point face actually exists, QuickDraw may scale that face to 9, 18, 24 (or whatever point size) by performing a linear scale of the 10 point face.
This can cause problems. Suppose a document is created on one Macintosh containing a font that only exists in that System file in one point size, say 9 point. The document is then taken to another Macintosh with a System file containing that same font but only in 24 point. The document may, in fact, appear differently on the two screens, and when it is printed, will have line breaks (and thus paragraph and page breaks) occurring in different places simply because of the differences in character widths that exist between the 9 point and 24 point faces.
The Printer Driver
Even when the printer you are using has a much higher resolution than what the screen can show, printer drivers perform line layout to match the screen layout as closely as possible.
The line layout performed by printer drivers is limited to single lines of text and does not change line break positions within multiple lines. The driver supplies metric information to the application about the page size and printable area to allow the application to determine the best place to make line and page breaks.
Printer driver line layout does affect word spacing, character spacing and even word positioning within a line. This may affect the overall appearance of text, particularly when font substitutions are made or various forms of page or text scaling are involved. But print drivers NEVER change line, paragraph or page break positions from what the application or screen specified. This means that where line breaks appear on the screen, they will always appear in the same place on the printer regardless of how the line layout may affect the appearance within the line.
Operating System and ROMs
In this context, operating system refers to the ROM trap routines which handle fonts and QuickDraw. Changes have occurred between the ROMs in the handling of fonts. Fonts in the 64K ROMs contain width tables (as described above) which are limited to integer values. Several new tables, however, have been added to fonts for the newer ROMs. The newer ROMs add an optional global width table containing fractional or fixed point decimal values. In addition, there is another optional table containing fractional values which can be scaled for the entire range of point sizes for any one face. There is also an optional table which provides for the addition (or removal) of width to a font when its style is changed to another value such as bold, outline or condensed. It is also possible, under the 128K ROMs, to add fonts to the system with inherent style properties containing their own width tables that produce different character widths from derived style widths.
One or all of the above tables may or may not be invoked depending on, first, their presence, and second, the mode of the operating system. The Font Manager in the newer ROMs allows the application to arbitrarily operate in either the fractional mode or integer mode (determined, in most cases, by the setting of FractEnable) as it chooses, with the default being integer. There is one case where fractional widths will be used if they exist even though fractional mode is disabled. When FScaleDisable is used fractional widths are always used if they exist regardless of the setting of FractEnable.
Differences in line layout (and thus line breaks) may be affected by any combination of the presence or absence of the optional tables, and the operating mode, either fractional or integer, of the application. Any of the combinations can produce different results from the original ROMs (and from each other).
The integer mode on the newer ROMs is very similar to, but not exactly the same as, the original 64K ROMs. When fonts with the optional tables present are used on Macintoshes with 64K ROMs, they continue to work in the old way with the integer widths. However, on newer ROMs, even in the integer mode, there may be variations in line width from what is seen on the old ROMs. In the plain text style there is very little if any difference (except if the global width table is present), but as various type styles are selected, line widths may vary more between ROMs.
Variations in the above options, by far, account for the greatest variation in the appearance of lines when a document is transported between one Macintosh and another. Line breaks may change position when documents created on one system (say a Macintosh) are moved to another system (like a Macintosh Plus). Variations are more pronounced as the number and sizes of various type styles increase within a document.
In all cases, however, a printer driver will produce exactly the same line breaks as appear on the screen with any given system combination.
Further Reference:
• The Printing Manager
• The Font Manager
• M.IM.LaserWriterOpt
Script Manager 2.0 Date & Time Problems
Text M.TE.ScriptDateTime
Written by: John Harvey February 1990
This Technical Note describes known bugs and features in and solutions to the date and time routines introduced in Script Manager 2.0.
From the beginning, the Macintosh’s ability to handle dates was limited to a rather small range—slightly more than a century. Enhancements to the Script Manager, introduced with System Software 6.0, extended this range to ±35,000 years. Unfortunately, there is a minor bug in one of the crucial calls and a “feature” that looks like a bug in another.
You Said It Would Be A Long Time
_LongSecs2Date, the routine that translates a LongDateTime to a LongDateRec, has a bug caused by using a variable that has not been properly initialized. This bug rears its ugly head when negative values are passed to the routine. System Software 6.0.4 and later fix this bug, and there is a simple solution for earlier systems.
If using System Software 6.0.3 and earlier, if you call _LongSecs2Date once before you really want to use it, the variable is cleared. After the initial call, _LongSecs2Date works correctly.
For example:
MPW Pascal
PROCEDURE DoDateStuff;
VAR
lsecs: LongDateTime;
ldr: LongDateRec;
BEGIN
InitDateCache(dcr);
lsecs := 0;
LongSecs2Date(lsecs,ldr);
{now you can call LongSecs2Date for real}
END;
MPW C
void DoDateStuff()
{
LongDateTime lsecs;
LongDateRec ldr;
/* work around the bug */
lsecs = 0;
LongSecs2Date(&lsecs,&ldr);
/* now call LongSecs2Date for real */
}
Any String To Date
The routine _String2Date was originally designed to be as forgiving as possible. It is so forgiving that it accepts any non-alphabetic character as a separator and accepts a single number as a valid date. For instance, if you pass _String2Date a string like “<20” it generously assumes that the less than sign (<) is intended as a divider and that “20” must be intended as a day, since there are only 12 months in a year. It returns a result of noErr and a date which is the twentieth of the current month in the current year. The string “<3*3” produces March 3 of the current year, while “4>1” politely gives the date April 1 of the current year.
This forgiveness really is not a bug, but a feature. Unfortunately it isn’t a feature that has been greatly appreciated in the developer community. For that reason, the rules for date and time dividers are tighter in System 7.0. Current thinking is that all list separators now used in 'itl0' resources will be allowed with a few common date separators used in the U.S. (e.g., colon (:) and hyphen (-)). For now, it is important to be aware of, shall we say, the flexibility of _String2Date and avoid thinking of it as an intelligent date parser. If you want to parse something, you can use _IntlTokenize.
Further Reference:
• Inside Macintosh, Volume V, The Script Manager
• The Script Manager 2.0, Interim Chapter (DTS)
Drawing Characters into a Narrow GrafPort
Text M.TE.TextInNarrowGP
Revised by: March 1988
Written by: Ginger Jernigan January 1986
When you draw a character into a GrafPort, your program will die with an address error if the width of the GrafPort is smaller than the width of the character. If you check before drawing the character to see if the GrafPort is wide enough, you can avoid this unfortunate tragedy.
Further Reference:
• QuickDraw
Fond of FONDs
Text M.TE.FONDs
Written by: Joseph Maurer May 1992
This Technical Note takes the place of Tech Note #26, “Character vs. String Operations in QuickDraw” by Bryan Stearns (March 1988), which pointed out the possible differences between the results of a StringWidth call and successive calls to CharWidth. This Note updates and brings into a broader context the issues related to text measuring. It also provides additional documentation on font family resources ('FOND's), and addresses various other frequently asked questions related to the Font Manager.
Introduction
Every Macintosh developer needs to draw text in a GrafPort, and to specify typeface, size, and style. In most cases, there are no problems, and application developers don’t need to have in-depth knowledge of the Font Manager’s inner workings and the data structures involved. Sometimes, however, the results on the screen or on printed output may be different from what you expected. Then, usually, DTS comes into play to figure out what the problem is and how to fix it. This Note is based on sharp developer questions from the last year or so, which point mainly at shortcomings of the existing Font Manager architecture, inconsistencies in its data structures, and missing details in the documen-tation.
We’ll start with a historical overview, which discusses the introduction of font family description resources ('FOND's) back in 1986, explains the consequences of non-proportionally scaling fonts, and covers non-registration and volatility of font family numbers.
We will then deal with the Font/DA Mover and the built-in “Mover” of the Finder in System 7. We discuss a number of not-so-well-known aspects of moving fonts in and out of a suitcase file, and recommend that you altogether abandon the resource type 'FONT'. We'll also comment on font names, and show you how to put separate stylistic variants of a typeface together into one font family. And we provide documentation on the ffVersion field of a 'FOND' (accompanied by a disclaimer and another piece of irritating information).
The main body of this Note addresses how the Font Manager works in the FMSwapFont context, and gives information on the scaling factors in the FMOutput structure and on the changes introduced by TrueType. We again took the examples of unexpected behavior (under certain circumstances) from developer questions. Thanks for helping document this!
Determining the width of text, as required for line layout, is sometimes trickier than you might think. We will document the effects of SetFractEnable in more detail and mention some more line layout problems.
Finally, this Note includes sample code that puts the OutlineMetrics call to work, and determines text bounding boxes for bitmap fonts.
Some FOND Background
Originally (Inside Macintosh Volume I, Chapter 7), all font-related data was contained in resources of type 'FONT'. For a font number within the range 0....255, and a font size restricted to less than 128, the (unnamed) 'FONT' resource with an ID:
128*(font number) + (font size)
contained the bitmap font strike, while the 'FONT' resource with ID = 128*(font number), corresponding to font size 0, did not contain any data, but its resource name provided the font family name. QuickDraw took care of stylistic variants like italic, bold, shadow, and so on; if a user had a specifically fine-tuned font strike for a stylistic variant, QuickDraw would not automatically substitute it when drawing text.
For aesthetic reasons, bitmap fonts for different sizes were usually designed with widths non-proportional to the point size. For example, the text “Show the difference in text widths” drawn with Courier 9 measures 170 pixels, whereas the same text drawn with Courier 18 measures 374 pixels, which is 10% more than you expect. (By the way, this is bad news for the ImageWriter printer driver. When “Best” mode (144 dpi) is selected and text in Courier 9 is to be printed, the printer driver uses Courier 18 to render the 9-point font size on the paper at twice the screen resolution, and obviously has big trouble compensating for the 10% difference in text width.)
On the other hand, given that only integer character widths (in QuickDraw’s 72 dpi units) are possible, proportional font scaling is compromised anyway. Accumulated rounding errors in text measuring, particularly for scaled fonts, contribute to the headaches of many Macintosh programmers. The computed text widths (vital for positioning text precisely and for line layout algorithms to justify text) sometimes change quite abruptly when the user removes or adds certain font sizes.
The introduction of the LaserWriter, and the success of Macintosh in the desktop publishing arena, required an extension of the original Font Manager architecture. This extension is based on the concept of “font family description” resources of type 'FOND', and on a new resource type 'NFNT' for the data of the existing 'FONT' resources (see Inside Macintosh Volume IV, Chapter 5).
The 'FOND' resource stores size-independent information about the font family, and its resource ID is the font number (in the range 0...32767). The resource name of the 'FOND' is the font name, and it contains a variable-length font association table, which references the font strikes belonging to a specific font family. These references include size, style, and resource ID of the 'NFNT' or 'FONT' resource containing the bitmap font data. TrueType fonts were retrofitted into this scheme, and are identified as font strike resources for point size zero. Any reference to point size zero refers to a resource of type 'sfnt'.
Note: The range 0...32767 for font numbers is subdivided into ranges for the various script systems (see Inside Macintosh Volume VI, pages 13-8 and 14-22, and M.TE.FontsAndScripts). This restricts the range of font numbers for the Roman script to 0...16383, with 0, 1, and 16383 reserved for the system.
Since Apple originally intended fonts to be referenced by their font family numbers, DTS attempted to register those numbers (see Inside Macintosh Volume I, page 219 and Volume IV, page 31). This failed—not only because the number of fonts registered grew greater than the number of font family numbers available, but also because the Font/DA Mover (version 3.8, shipped with System 6), and the “Mover” built into the System 7 Finder resolve conflicts between font IDs (which happened anyway!) by renumbering the fonts on-the-fly. There is no font ID registration any more—except for the very special case of Japanese Kanji 'FOND'–'fbit' IDs, and potentially for Korean, Chinese and other double-byte fonts.
As early as April 1988, M.IM.FontNames recommended the use of font names rather than font family numbers. Since then, the recommendation has been reinforced in Inside Macintosh Volume VI, page 12-16. Fortunately, most applications have been good about following this recommendation. Unfortunately, some exceptions remain, even in Apple’s own software. QuickDraw Pictures created without 32-Bit QuickDraw refer to fonts by font family number only!
For obvious reasons of upward compatibility (to maintain existing fonts, and to avoid reflowing of existing documents), the introduction of 'FOND's did not solve all the problems. This is what this Note is all about.
Moofing Fonts
The Font/DA Mover utility has evolved into version 4.1, which knows about 'sfnt's. It is available on the Developer CD Series disc, path “Tools & Apps (Moof!): Misc. Utilities:”. The Finder in System 7 incorporates its own “Mover” (see Inside Macintosh Volume VI, page 9-33), which makes the Font/DA Mover redundant for System 7 users.
Given the combinatorial explosion of all imaginable situations with 'FOND's, 'FONT's, 'NFNT's and 'sfnt's, and stylistic variations of fonts belonging to the same family, the font moving job deserves respect. The following notes cover some less well-known aspects of this business.
• If an old “standalone” 'FONT' (without corresponding 'FOND' resource) is moved into a suitcase file, Font/DA Mover or the System 7 Mover creates a minimal 'FOND' resource on-the-fly. This 'FOND' has no tables, and nearly all its fields are zeroed. The System 7 Finder also converts the resource type from 'FONT' to 'NFNT'; unfortunately, the Font/DA Mover keeps the resource type 'FONT'.
Note: While it is perfectly legal to have 'FOND's continue to reference the older 'FONT' type, DTS recommends that you avoid 'FONT's. Accessing 'FONT's is much slower, since the Font Manager always looks for 'FOND's and 'NFNT's first. More importantly, 'FONT's are troublemakers if an application comes with its own font in its resource fork. Imagine an application that includes a private 'FOND' which references a 'FONT' in its resource fork by resource ID. When the Font Manager wants to load the font resource, it first looks for a resource of type 'NFNT' with this same resource ID. If there’s an 'NFNT' in the System file with the same resource ID, the Font Manager will pick it instead of the 'FONT' from the application’s resource fork. This happens more often than you’d like to think!
• Under the current font architecture, the font name is the resource name of the 'FOND' resource (let’s forget about 'FONT's altogether), so the font name can be any Pascal string. Unfortunately, this conflicts with the 31-character limitation of a file name when the System 7 Finder derives the file name of a movable font file (Inside Macintosh Volume VI, page 9-34) from the font name. Some third-party fonts come with font names long enough to cause trouble. You may also see this problem when trying to open a suitcase if the Finder can't generate distinct names for all of the fonts in the suitcase; the Finder may say the suitcase is “damaged” when it is not.
Note: Each TrueType 'sfnt' resource contains a Naming Table (see The TrueType™ Font Format Specification, APDA™ M0825LL/A) which provides nearly unrestricted font naming capabilities, to accommodate the needs of font manufacturers. A forthcoming Macintosh Technical Note on TrueType Naming Tables gives additional information.
• QuickDraw and the current Font Manager have no provision for stylistic variants like “light,” “medium,” “demi,” “book,” “black,” “heavy,” “extra,” “ultra,” etc., used in the context of professional typesetting. Therefore, each of these variants comes with a separate font family resource. Probably for reasons of consistency, the “italic” variants have their own font family resources as well. Unfortunately, unless each 'FOND' references both the “plain” and the “italic” font strikes, QuickDraw will no longer know a customized italic font strike exists.
It is fairly easy, using System 7 and ResEdit, to merge two font families (named, for exmaple, “myFont” and “myFont italic”) into one. This way, QuickDraw will automatically use the pre-designed italic font strike instead of creating one algorithmically. Follow these convenient steps:
1. Make sure there is no resource ID conflict between the 'NFNT's and 'sfnt's belonging to both families.
2. Make sure the style bits for italic are set in the font association table of “myFont italic.”
3. From ResEdit’s File menu, “Get Info...” on the “myFont” 'FOND' resource. Write down the resource ID of the “myFont” 'FOND'.
4. From ResEdit’s File menu, “Get Info...” on the “myFont italic” 'FOND'. Change its resource ID to be identical to the one you wrote down in step 3. Change its resource name to “myFont.”
5. Use the Finder in System 7 to move the contents of the “myFont italic” suitcase into the original “myFont” suitcase. It will merge all constituents into one font association table, and thus enable transparent substitution of the right font for QuickDraw’s italic style.
Version Numbers
The 'FOND' structure (see Inside Macintosh Volume IV, page 45, “FamRec”) contains a field ffVersion, and inquiring minds naturally want to know more about it. Before anything else, however, please read the following disclaimer:
Disclaimer: The Font Manager does not check version numbers in a 'FOND', and we recommend that you not rely on the (intentionally vague) statements below, but rather analyze the data in the 'FOND'independently.
Currently, values 0...3 may appear in the ffVersion field, with the following intended interpretations:
Version 0: Usually indicates that the 'FOND' has been created on the fly by the Font/DA Mover (or the System 7 Finder). But the 'FOND' for Palatino on the distribution disks of System 7 is a counterexample.
Version 1: Obviously indicates the first version when 'FOND's came out (Inside Macintosh Volume IV, page 36).
Version 2: Corresponds to the extension of the 'FOND' format documented in Inside Macintosh Volume V, page 185 (which does not mean that the 'FOND' actually contains a bounding box table).
Version 3: The 'FOND' is supposed to contain a bounding box table.
This brings up an annoying fact. All measurement values (referring to a hypothetical 1-point font) in the 'FOND' are in a 16-bit fixed-point format, with an integer part in the high-order 4 bits and a fractional part in the low-order 12 bits. You would expect that negative values (like for ffDescent, or in the kerning tables) are represented in the usual two’s-complement format, such that standard binary arithmetic applies. This is mostly true, but not always. Again, Palatino is a counterexample (and probably not the only one). To our knowledge, version 0 and version 1 'FOND's have negative values represented in a format where the most significant bit is the sign bit, and the rest represents the absolute value. However, there is nothing in the system software that enforces this, so counterexamples may exist.
Warning: Don’t rely on the version number, but include sanity checks for the negative values in a 'FOND' instead! The following Pascal function shows how this can be done:
FUNCTION Check4p12Value(n: Integer): Integer;
{ n is a 4.12 fixed-point value; i.e., its "real" value is n/4096. }
{ If n is "unreasonably negative," interpret the most significant bit }
{ as sign bit, and convert to the usual two's complement format. }
BEGIN
IF n < $8FFF THEN { means: (4.12-interpretation of n) is below - 7 }
Check4p12Value := - BitAnd(n,$7FFF)
{ i.e., mask sign bit, and take negative of absolute value }
ELSE
Check4p12Value := n;
END;
In the Heart of the Font Manager
Swapping Fonts
As stated in Inside Macintosh, there is only one contact between QuickDraw and the Font Manager: the FMSwapFont function. Each of the three QuickDraw text measuring functions (CharWidth, StringWidth and TextWidth) always ends up in the QuickDraw bottleneck procedure QDProcs.txMeasProc. Each of the three QuickDraw text drawing procedures (DrawChar, DrawString and DrawText) always ends up in the QDProcs.textProc bottleneck procedure. Any reasonable textProc (like StdText) needs to call the currently-installed text measuring bottleneck procedure before actually rendering the text. And what does any reasonable text measuring bottleneck procedure(like StdTxMeas) do first, before anything else? It calls FMSwapFont, to make sure we are talking about the right font and its properties! (To be precise, GetFontInfo and FontMetrics are the other calls that make sure the right font is swapped in and set up, without requiring you to call FMSwapFont explicitly.)
Responding to a font request is a lot of work, and FMSwapFont has been optimized to return as quickly as possible if the request is the same as the previous one. Building the global width table (see Inside Macintosh Volume IV, page 41) is among the more time-consuming tasks related to FMSwapFont; this is why the Font Manager maintains a cache of up to 12 width tables.
Inside Macintosh Volume I, page 220 documents the Font Manager’s choice when a font of the requested size is not available. However, some consequences or additional features have occasionally been a surprise to developers (and users as well).
Scaling Factors in FMOutPut and StdTxMeas
Let’s suppose you have only a 12-point bitmap version of Palatino, and don’t have any Palatino outline fonts. When you request Palatino 18, QuickDraw sets up the FMInput record with size = 18 and numer = denom = Point($00010001).On return, the FMOutput record contains the handle to the font record to use (the 'NFNT' with the Palatino 12 bitmap font strike), and indicates the scaling factors QuickDraw will have to use to produce the desired text point size in FMOutput.numer and FMOutput.denom. In this example, that ratio is 3/2.
Note that these are also the values returned in StdTxMeas (Inside Macintosh Volume I, page 199) if you call the procedure with numer = denom = Point($00010001). Why? Because StdTxMeas calls FMSwapFont, as explained under “Swapping Fonts.” StdTxMeas does not apply these scaling factors to the text it measures. In our example, it would measure Palatino 12 and return numer and denom in the ratio 3/2 to tell you that your application must multiply the results by these values to get the correct measurements for Palatino 18. This has surprised more than one programmer who didn’t expect numer and denom to change!
By the way, the Font Manager always normalizes the scaling factors as fractions numer/denom such that the denominator is equal to 256. In our example, the real numbers returned by FMSwapFont or StdTxMeas are numer = 384 and denom = 256.
Warning: If the scaling factors numer and denom passed to StdTxMeas, StdText (see Inside Macintosh Volume I, pages 198 and 199), or in the FMInput record to FMSwapFont are such that txSize*numer.v/denom.v is less than 0.5 and rounds to 0, and if there is more than one 'sfnt' resource referenced in the font association table, then the current Font Manager may get confused and return results for the wrong font strike.
TrueType Always Has the Right Size
The default value of outlinePreferred is FALSE. If you have bitmap fonts for Palatino 12 and Palatino 14 in your system as well as a Palatino TrueType font, then requests for Palatino 12 or Palatino 14 are fulfilled with the bitmap fonts, but requests for any other size are fulfilled with the TrueType font. In particular, if you (or, for example, a printer driver) need Palatino 12 scaled by 2, the Font Manager will actually look for Palatino 24 and return the outline font, regardless of the setting of outlinePreferred. Even if you wanted the bitmap font doubled for exact “what-you-see-is-what-you-get” text placement, you’re out of luck—you get the TrueType font, which may have very different font metrics or character shapes.
If the Font Manager uses an outline font to fulfill a given font request, the IsOutline function returns TRUE. Interestingly, this does not imply that RealFont returns TRUE as well. If the text size is smaller than the value lowestRecPPEM (“smallest readable size in pixels”) in the 'head' font header in the TrueType font (see The TrueType Font Format Specification, version 1.0, page 227), then RealFont returns FALSE!
First Size, Then Style—or: To Be or Not to Be Outline
When the Font Manager walks the font association table of a 'FOND' to look for a font strike of a specified size and style, it stops at the first font of the right size. Only if you requested a stylistic variant (like bold or italic) does it take a closer look at the fonts of the same size. It does this by putting weights on the various style bits (for example, 8 for italic, 4 for bold, 3 for outline) and choosing the font strike whose style weight most closely matches the weight of the requested style. All this is fine when only bitmap fonts are available. With the presence of TrueType outlines, however, the results are not always as expected, depending on the font configuration installed.
Let’s look at a few examples:
Example 1: Let’s suppose you have the bitmap font Times 12 (Normal) and the TrueType fonts Times (Normal), Times Italic and Times Bold in your system. If you request Times 14 Italic or Times 14 Bold, it’s rendered from the Times Italic or Times Bold TrueType fonts. However, if you ask for Times 12 Italic or Times 12 Bold, and your system has the default setting of outlinePreferred = FALSE, the Font Manager decides to take the Times 12 bitmap and let QuickDraw algorithmically slant it (for italics) or smear it (for bold).
Example 2: Let’s suppose you want to draw big, bold Helvetica characters and there are no existing bitmaps for the size you want. If the Helvetica Bold TrueType outlines are available, the Font Manager chooses them and the only surprise in text rendering will be a pleasant one. If there is no Helvetica Bold TrueType font, however (like in the machine of your customer, who kept only the normal Helvetica TrueType font in his system), then the characters are rendered using the normal Helvetica outlines and, in a second step, QuickDraw applies its horizontal 1-pixel “smearing” to simulate the bold stylistic variant. The result is very different (and rather an unpleasant surprise).
Example 3: Admittedly, this is less likely (but it has happened). Let’s suppose somebody decides to rip the Times TrueType outline out of the System file (don’t ask me why—I don’t know). He forgets to take the Times Italic TrueType outline away as well. The next time he draws text in Times (Normal), in a size for which there is no bitmap font (or if outlinePreferred = TRUE), the Font Manager goes for an 'sfnt', and the text shows up in italic (what a surprise!).
Unfortunately, given the current implementation of the Font Manager, there are no solutions to the problems illustrated above—other than asking users of your application to install the fonts you recommend. The only way to anticipate these potential surprises from within your application is to look into the 'FOND's font association table. You can’t depend on the IsOutline function because it returns TRUE as soon as the Font Manager stops at an 'sfnt', in its first pass through the font association table—regardless of subsequent stylistic variations. This means, for example, if you ask for Helvetica Bold and IsOutline returns TRUE, you don’t know if you got the Helvetica Bold TrueType font or if QuickDraw “smeared” the Helvetica (Plain) TrueType font.
Where Do the Widths Come From?
Text measuring (for example, for precise text placement in forms with bounding boxes) and most line layout algorithms for justified text rely heavily on the character widths contained in the global width table. Given that under the current font architecture, we may easily have three or more different width tables for the same font specification (the non-proportional integer widths attached to the 'NFNT', the fractional widths contained in the 'FOND', and the fractional widths provided by the 'sfnt'), it is important to understand where the widths come from in any case.
Since SetFractEnable was introduced (Inside Macintosh Volume IV, page 32 and Volume V, page 180), its setting TRUE or FALSE was supposed to give predictable effects. If it’s FALSE, the Font Manager takes the integer widths from the 'NFNT'; if it’s TRUE, it takes the fractional widths from the 'FOND'. Unfortunately, there are some additional details and side effects that are not well known.
• The Font Manager looks at bit 14 of the ffFlags field in the 'FOND' (see Inside Macintosh Volume IV pages 36 and 37). If it is set (like it is for Courier), the fractional widths from the 'FOND' are never used.
• If SetFractEnable is TRUE and you request a stylistic variation like bold or italic, the Font Manager looks at bits 12 and 13 of the ffFlags field to decide how different widths or extra widths for the stylistic variants have to be used. What it decides is documented in the “Font Manager” chapter of Inside Macintosh Preview, located on the Developer CD Series discs.
• Given that it is not possible to set the pen to a fractional position, precise text positioning with fractional widths enabled is always compromised because of (accumulated) rounding errors.
• QuickDraw distributes the accumulated rounding errors across characters within a string (instead of adding it at the end of the drawn text). This results in poor text quality on the screen, and in problems when calculating the position of the insertion point between characters.
• The LaserWriter driver watches what you pass to SetFractEnable. Passing TRUE to SetFractEnable disables some of the LaserWriter driver’s line layout features, assuming that the programmer intends to control text placement manually. Explicitly passing FALSE to SetFractEnable achieves different results than using the default value of FALSE—Font Substitution behaves differently, for example. These effects are sometimes Not What You Wanted.
• On non-32-Bit-QuickDraw systems, SetFractEnable is not recorded in pictures. This affects the line layout of text reproduced through DrawPicture if the picture was created with fractional widths enabled.
In systems with TrueType, quite naturally the widths always come from the 'sfnt' when the Font Manager uses a TrueType font. If fractEnable is FALSE, hand-tuned integer character widths for specific point sizes come from the 'hdmx' table in the 'sfnt'. If fractEnable is FALSE and no 'hdmx' table is present or it contains no entries for the desired point size, the fractional character widths from the 'sfnt' are rounded to integral values.
More Line Layout Problems
The routines SpaceExtra (Inside Macintosh Volume I, page 172) and CharExtra (Inside Macintosh Volume V, page 77; available only in color GrafPorts) are intended to help you draw fully justified text. This works fine on the screen, but not all printer drivers are smart enough to use these settings appropriately under all circumstances. In particular, if you pass TRUE to SetFractEnable, or if you turn the LaserWriter driver’s line layout algorithm off (by means of the picture comment LineLayoutOff; see Macintosh Technical Note #91), or if font substitution is enabled and actually occurs, it is better not to rely on SpaceExtra and CharExtra when printing fully justified text. Instead, keep the LaserWriter driver’s line layout adjustments off, and calculate the placement of your text (word by word, or even character by character) yourself.
Putting Text Into Boxes
TrueType fonts came to the Macintosh together with seven new Font Manager routines (as documented in Inside Macintosh Volume VI, Chapter 12). The OutlineMetrics function is certainly the most sophisticated of these, and sample code illustrating its usage may be helpful. The following procedure DrawBoxedString assumes that the new outline calls (Inside Macintosh Volume VI, Chapter 12) are available, and that IsOutline returns TRUE for the current port setting.
PROCEDURE DrawBoxedString(pt: Point; s: Str255);
{ Draw string s at pen position (pt.h, pt.v), and show each character's bounding box. }
CONST
kOneOne = $00010001;
VAR
advA: FixedPtr;
lsbA: FixedPtr;
bdsA: RectPtr;
err,i,yMin,yMax,leftEdge,temp: Integer;
numer,denom: Point;
advance,lsb: Fixed;
r: Rect;
BEGIN
numer := Point(kOneOne);
denom := Point(kOneOne); { unless you want to draw with scaling factors
.... }
MoveTo(pt.h,pt.v);
DrawString(s);
{ This is for the pleasure of your eyes only — in practice, you would probably }
{ first look at the metrics, and then decide where and how to draw the string! }
{ Add accumulated advanceWidth and leftSideBearing of current glyph }
{ horizontally to starting point. }
leftEdge := pt.h + Fix2Long(advance + lsbA^);
r := bdsA^; { The bounding box rectangle is in TrueType coordinates. }
temp := r.bottom; { need to flip it "upside down" }
r.bottom := - r.top;
r.top := - temp;
OffsetRect(r,leftEdge,pt.v);
FrameRect(r); { This is the glyph's bounding box. }
advance := advance + advA^;
{ "Advance" is Fixed, to avoid accumulation of rounding errors. }
{ Now, bump pointers for next glyph. }
bdsA := RectPtr(ord4(bdsA) + SizeOf(Rect));
advA := FixedPtr(ord4(advA) + SizeOf(Fixed));
lsbA := FixedPtr(ord4(lsbA) + SizeOf(Fixed));
END;
DisposPtr(Ptr(advA));
DisposPtr(Ptr(lsbA));
DisposPtr(Ptr(bdsA));
END; { DrawBoxedString }
OutlineMetrics exists because many developers need pixel-precise information on placement and bounding boxes, often on a character-by-character basis. Unfortunately, there is no similar facility for text drawing with bitmap fonts. Worse, under certain circumstances, italicized or shadowed (or both) bitmap fonts are sometimes poorly clipped, particularly for scaled sizes. Cosmetic workarounds include adding a space character to strings drawn in italic. You might also draw the text off-screen first (in order to determine the bounding box of the black pixels) and use CopyBits to copy the text onto the screen—but using CopyBits for text is usually bad for printing.
The existing documentation on the FMOutput and global width table structures (Inside Macintosh Volume I, page 227 and Volume IV, page 41) suggests it’s possible to devise a routine for determining a fairly precise text bounding box for bitmap fonts. The procedure below, BitmapTextBoundingBox, is a first attempt. It assumes that TrueType is unavailable, or that the IsOutline call returned FALSE for the current port settings. While the returned bounding box is not always “tight,” be careful before modifying the algorithm and shrinking the resulting bounding box—bitmap fonts just don’t contain enough precise information for an exact bounding box, and different bitmap fonts and different sizes may require different adjustments.
PROCEDURE TextBoundingBox(s: Str255; numer,denom: Point; VAR box: Rect);
CONST
FMgrOutRec = $998; { FMOutRec starts here in low memory }
tabFont = 1024;
{ global width table offset for font record handle, see IM IV-41 }
TYPE
FontRecPtr = ^FontRec;
VAR
hScale,vScale: Fixed;
err,intWidth,kernAdjust: Integer;
xy: Point;
info: FontInfo; { only for StdTxMeas; we'll use FontMetrics }
At the time when the original Font Manager architecture was designed, based on QuickDraw’s hard-coded 72 dpi resolution, nobody could anticipate that some years later, the Macintosh would be used to tackle professional typesetting projects. Several advanced page layout applications managed to work around the “built-in” limitations, at high development costs, and some compatibility and performance problems. In many other cases, however, those limitations caused questions to DTS and unsatisfying compromises. This Note can’t do much more than explain the state of affairs; the real solution to the problems must come from a redesigned foundation. TrueType leads the way and already fulfills many of the requirements; everything else is getting closer and closer.
Further Reference:
• Inside Macintosh, Volume I, Chapter 7, The Font Manager
• Inside Macintosh, Volume IV, Chapter 5, The Font Manager
• Inside Macintosh, Volume V, Chapter 9, The Font Manager
• Inside Macintosh, Volume VI, Chapter 12, The Font Manager
• New & Improved Inside Macintosh, Imaging: The Font Manager. Developer CD Series disc, path Developer Essentials: Technical Docs: Inside Macintosh Previewf
• Macintosh Technical Note #91, Picture Comments—The Real Deal
• M.IM. FontNames
• M.TE.FontsAndScripts
• M.IM.FontFamilies
• Apple LaserWriter Reference, Chapter 2, Working With Fonts ( Addison-Wesley, 1988)
• Adobe Technical Note #0091 (PostScript Developer Support Group), Macintosh FOND Resources
PostScript and Adobe are registered trademarks of Adobe Systems Incorporated.
Helvetica and Palatino are registered trademarks of Linotype AG and/or its subsidiaries.
Velocio is not a trademark of the author.
Font Manager Q&As
Text M.TX.FontMgr.Q&As
Revised by: Developer Support Center October 1992
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As and Q&As revised this month are marked with a bar in the side margin.
Chicago Control-Q prints propeller or clover symbol
Written: 7/4/90
Last reviewed: 8/1/92
How do I get the character that represents the clover used for command-key equivalents in documents and in menus?
___
This key is documented in the Apple Style Guide, which is available on the latest Developer CD Series disc as well as from APDA. One little feature of Key Caps which is not widely known is the Control key (not the Command or Option keys). Pressing the Control key in Chicago shows that Control-Q in Chicago maps to the propeller symbol for which you search. Control-Q generates the character code 17; the standard Macintosh character set (see Inside Macintosh Volume VI, page 12-5) specifies this symbol for it.
Determining Macintosh system font size
Written: 2/11/91
Last reviewed: 8/1/92
How does a program determine the default system font size?
___
If you want to know the default system font size, use the GetDefFontSize call (Inside Macintosh Volume V, page 314). This call will return the true default system font size. On all Roman systems the sysFontSize low-memory global is always zero by default; this means that it’s actually 12. Don’t ask me why they did this, but it’s true, so you should use the call if you want a painless method of obtaining this information. Try to avoid reading the global directly.
FScaleDisable and Macintosh screen rendering
Written: 6/4/91
Last reviewed: 8/1/92
What is the current interface guideline on font scaling? Should we set FScaleDisable to true or false for screen rendering? I believe the old guideline was to set it to true (thus disabling font scaling) so that if you have a 23 pt screen font you will see 12 pt glyphs with 23 pt widths. We want performance on Apple’s low-end machines, and we don’t want to make this a user preference.
___
Most applications now set FScaleDisable to false all the time. If you are worried about the speed degradation in your application when running on slower Macintosh systems, you could have your software identify which Mac it is running on using Gestalt, SysEnvirons, etc., and then set FScaleDisable to true only on the slow machines. From the purely interface point of view, “what-you-see-is-what-you-get” is best, if processor speed allows it.
With TrueType there is no issue, since FScaleDisable doesn’t have any effect on an outline font.
Macintosh double-byte character encoding
Written: 8/5/91
Last reviewed: 8/1/92
When will System 7 and TrueType be able to support a larger character encoding vector than the current 256 characters? Is this something I could do now?
___
System 7 does not have double-byte character encoding support now. In the future, Apple will use the two-byte UNICODE standard for its operating systems. However, such a change will be huge and therefore will not be available in the near future.
Ever since 6.0.2, KanjiTalk has had double-byte encoding, so you can use the Japanese system at this time. The system which allows you to do this is called shift-JIS (Japanese Input System). The mappings are on the current Developer CD in the Kanji folder.
'FOND' resource features subject to change
Written: 6/17/91
Last reviewed: 8/1/92
Which 'FOND' features are subject to change, and what parts can I rely on?
___
'FOND' features not documented in Inside Macintosh, Macintosh Technical Notes, or develop are subject to change. That’s the official word.
Five font style-mapping table styles
Written: 6/17/91
Last reviewed: 8/1/92
Why is “47” the bounds of the indexes’ array in the StyleTable?
___
The style-mapping tables, which are used for mapping of a font style to a particular font for printing, have only five possible styles, unlike the six screen styles (underline is omitted, since a laser printer just explicitly draws a line under the string—it doesn’t need a special font for that). You can have any combination of these five styles, EXCEPT that you can’t have “Condense” and “Extend” at the same time (Condensed Extended wouldn’t make a whole lot of sense). Thus you get (combinatorics come back to haunt us) 48 possible unique styles to map to.
We ship modified versions of the Chicago and Geneva fonts and their FONDs in our application’s resource fork. With System 6, when we ask for font 0 or font 3, we get our modified fonts in windows, buttons, menus and dialogs, but not with System 7. Is there a new way to tell System 7 to use my version of Chicago 12 to display all system related stuff?
___
What’s happened is that the system software is being much more strict about whose “Chicago” it uses for menus and dialogs. The Menu and Control Managers now only look at the system file for the Chicago they use.
There are a couple of ways to get around this: First, you can try patching DrawString right before calling MenuSelect in your program. The patch would select your Chicago and then jump to the standard DrawString. After MenuSelect, remove the patch. The disadvantage of this method is that, if a future system software release doesn’t use DrawString for drawing menus, the patch would cease to have any effect.
A better solution is to write your own menu and control definition code—in other words, your own custom MDEF and CDEFs. The way you typically do this is to get a copy of the standard Macintosh system’s MDEF or CDEF, and alter it to your specifications. In your case, this would be merely selecting your font instead of the system font. Name your font something other than Chicago and just select that font by name in the menu or control’s draw routine. The Control and Menu Manager chapters in Inside Macintosh Volume I have more information on writing custom definitions. There is only one problem with this right now: While the System 6 MDEF and CDEF are available on AppleLink, the System 7 versions are not available yet, although they will be soon.
A note on the use of Chicago in your application: As the June 1991 edition of the Macintosh Technical Note “Font Family Numbers” mentions, fonts are copyrighted material. Apple owns the Chicago font and typeface, so be sure you check into licensing issues before releasing any version, altered or unaltered, with your application.
Spanish typographic measurements
Written: 12/10/91
Last reviewed: 8/1/92
What typographical measurement issues must be considered for Spanish systems? Do the Spanish specify type in ciceros and didots instead of points?
___
If typesetting is done in Spain with computers, U.S. standards generally are used. It is only when typesetting is done the old-fashioned way that you’ll see different measurements.
Paper sizes are different. In Spain DIN-44 (210 x 297), DIN-A3 (420 x 297) and “folio” (215 x 315) are used. As always, you’ll be working with 72 dpi for the screen (and any time you use QuickDraw) but something different on paper, so you’ll need to use PrGeneral and image the stuff yourself to a resolution that allows you control over your imaging for printing. This is detailed in the article, “Meet PrGeneral, the Trap That Makes the Most of the Printing Manager,” in issue #3 of develop.
Here are the measurements used in Spain:
• Decimal point (Didot) (0.3759 mm)
• Millimeter
• Cicero (12 decimal points)
• Centimeter
• Inches
SetOutlinePreferred = TRUE or not?
Written: 12/2/91
Last reviewed: 8/1/92
My application calls SetOutlinePreferred so outline fonts are used if both bitmapped and TrueType fonts are in the system. It was reported to me, however, that some international TrueType fonts in particular look really bad at small point sizes on the screen. Should I avoid calling this function?
___
SetOutlinePreferred is best used as a user-selectable option. Along the same lines, you might want to include the SetPreserveGlyph call (Inside Macintosh Volume VI, page 12-21), again, as a user-selectable option.
Currently, as you know, the default for outlinePreferred is FALSE; this is for compatibility reasons (existing documents don’t get reflowed if the bitmap fonts are still around) and for esthetic and performance reasons (users are free to maintain bitmap fonts in the smaller point sizes if the TrueType version is not satisfying for small sizes, or too slow). On the other hand, as soon as a bitmap font is *un*available for a requested point size, and an outline font is present, the outline font is used even with outlinePreferred = FALSE. Setting outlinePreferred = TRUE makes a difference only for point sizes where a bitmap font strike is present along with a 'sfnt' in the same family/style.
TrueType fonts might be preferable even for small point sizes if linearly scaled character widths are more important than screen rendering: If the main purpose of a program is preprint processing for a high-resolution output device, then outlinePreferred = TRUE may give better line layout results on the printer, at the price of “not so great” type rendering on a 72 dpi screen. (An example for the conflict between linearly scaling TrueType and non-linearly scaled bitmap fonts is Helvetica: StringWidth('Lilli') returns 19 for the 12-point bitmap font, and 15 for the 13-point size from TrueType!)
All this boils down to the recommendation stated initially: The user should be given the flexibility to decide whether to use the existing bitmaps (using TrueType only for bigger point sizes and high-resolution printers), or to go with TrueType even if the result on the screen is not optimal. (By the way, it’s likely that TrueType development will substantially reduce this conflict in the future.)
FontRec fontType field and determining monospaced fonts
Written: 1/13/92
Last reviewed: 8/1/92
How can I create a menu that contains only fixed width fonts? The FontRec record’s fontType field doesn’t correctly tell me if the font is fixed width as Inside Macintosh Volume V says it should. All system fonts appear to have the same fontType regardless of whether they are fixed or proportional. Currently I test if the width of the characters “m” and “i” are equal and if they are, I consider the font to be fixed width. Is there an easier (and faster!) way?
___
The Font Manager documentation is not explicit enough about the fact that bit 13 (0x2000) of the fontType field is basically useless. Neither does the Font Manager check the setting of this bit, nor does QuickDraw (or any printer driver). As you observed, monospaced fonts like Monaco or Courier don’t have the bit set; so the meaning of this bit is just perverted to nonsense—sorry! In addition, the fontType field only is available for 'FONT's and 'NFNT's; it does not exist in 'sfnt's, and you would have to check separately for the resource type of the font.
Your idea of comparing the widths of “m” and “i” (or any other characters which are extremely unlikely to have the same widths in a proportionally spaced font) is indeed the only reasonable way of figuring out if a font is monospaced.
Getting global width table for a font specification
Written: 4/22/92
Last reviewed: 8/1/92
What’s the fastest way to get the width table for a given font? FontMetrics is too slow, especially in color. Is there any other call that will get the global width table set up correctly? That is all that we need from the call to FontMetrics.
___
FontMetrics does not have much overhead in setting up the width table. It does a dummy DrawChar(' '); this is very rapidly transformed into a call to StdText, and StdText immediately calls StdTxMeas. The first thing StdTxMeas does is to set up the input parameters for a FMSwapFont call and call it. FMSwapFont is the heart of the Font Manager, and does all the work. It comes back with the FMOutput record, the font strike, and the width table. From there, FontMetrics derives the values it needs to bring back.
You probably wouldn’t save much more than 1/1000th of a second if, instead of calling FontMetrics, you called FMSwapFont explicitly yourself—our only alternative suggestion.
The Font Manager does quite a lot of caching (up to 12 width tables), and managing the cache takes some cycles, too. If there is nothing in the cache corresponding to the font request, the cache makes the call even slower than it would be without cache.
The next source of overhead is the Resource Manager. Looking for a specific font involves going through the whole resource chain first for the FOND (if none is found, the search restarts for a FONT), and then, based on the FOND’s font association table, for a NFNT or 'sfnt'. If no NFNT is found, the search restarts for a FONT, always through the whole resource chain. For a huge resource fork like in the System file (and, maybe, also in your application), the time spent in the Resource Manager is not negligable—in particular, if you have add-ons in your system (such as INITs or 'cdev's) that patch out Resource Manager calls, maybe several times, and usually slow it down considerably!
Even in case this hurdle is overcome swiftly (after all, the Resource Manager has its own caching scheme for optimization), the next step necessarily takes some time, and, as you have observed, especially on a color system: It consists of actually providing the bitmap for the font strike. If the screen depth is >1, this involves creating “synthetic” fonts for the correct screen depth, to optimize text drawing. Also, if the font is an outline font, the first time a font strike has to be rasterized is quite costly in terms of machine cycles.
Finally, the width table can be created; and, because of the scaling factors involved, this requires 256 times some arithmetic which is known never to be fast enough.
All this certainly gives us an understanding for the time it takes FMSwapFont (FontMetrics) to get the job done, but it does not solve your problem.
Depending on how predictable the usage of fonts and width tables in your application is, you might consider building kind of a database of width tables beforehand, or along the way, and use this information directly from within your application. There is no shortcut at all through the Resource Manager to get at the font resources, and there is no shortcut within FMSwapFont, like not building the font bitmaps. (To the best of my knowledge, the needbits field in the FMInput record does *not* have this effect.) The only obvious way to get width tables faster is to keep them around, and to extend manually the capacity of the Font Manager’s cache of width tables.
SetFractEnable and recalculating width tables
Written: 5/5/92
Last reviewed: 8/1/92
Calling SetFractEnable seems to force the width tables to be recalculated regardless of the setting of the low-memory global FractEnable. We’re calling this routine at a central entry point for any document, as it’s a document by document attribute. We then unconditionally call SetFractEnable(false) on exit back to the event loop, to be nice to other applications. Calling SetFractEnable(false), seems to trigger the recalculation even though FractEnable is false. What’s the best way to get around this?
___
Your observation is correct. The SetFractEnable call stuffs the boolean parameter (as a single byte) into the low-memory global $BF4 and indiscriminately invalidates the cached width table by setting $B4C (LastSpExtra) to -1 (LongWord = Fixed). Obviously, it was not anticipated that SetFractEnable could be called quite regularly with a parameter that often does not change the previous setting. (By the way, the same observation applies to the SetFScaleDisable call).
In your case, you may want to replace the SetFractEnable call by your own test of the boolean (8-bit) in $BF4 (FractEnable), and call SetFractEnable only if the parameter passed is different from the value stored in $BF4. Note that Inside Macintosh Volume IV (page 32) explicitly allows you to hack the $BF4 location directly, so it’s unlikely there are any future compatibility problems if you go your own way around the original SetFractEnable. The only additional information you need is what’s mentioned above: The Font Manager always checks $B4C (LastSpExtra) for -1 before doing anything with the global width table; if it finds ($B4C) = LongInt(-1), it painfully rebuilds the width table.
Another comment: You do not need to think of other applications when resetting FractEnable; in a context switch to another application, all low-memory globals are swapped anyway. Still, the above optimization of SetFractEnable probably is useful even when you don’t call it any more systematically on exit to the event loop.
Corrupted Macintosh font or font suitcase criteria
Written: 6/19/92
Last reviewed: 9/15/92
I would like to be able to detect whether a font suitcase is corrupted when it is opened and whether any of the fonts in it are corrupted before any of the fonts are used. I know that the Finder is able to do this, and I was wondering if Apple gives out this information. My program will only run under System 7.0 if that helps. Any information that you can give me would be greatly appreciated.
___
The Finder and the type architecture are living things; the definition of what is and is not a damaged suitcase can change from release to release of system software. However, any of the following conditions makes System 7.0 report the suitcase as “damaged”:
• More than eight FONDs reference the same font.
• A new stand-alone object can’t be created for a font icon. The usual cause of this is that two FONDs have the same name for the first 31 characters, and the Finder thinks there’s already an icon in that window with the same name. (Two icons in the same directory with the same name is a sign of damage.)
• There must be at least one font association table entry, and the table can’t go past the logical end of the resource.
• The first resource name in the map must not be zero-length (which is a test for some older third-party corrupted suitcases).
• The FOND must have a name.
• The FOND must have a valid character range—the first character has to be less than the last character—unless it is a "dummy" FOND (created on the fly for old standalone FONTs; in this case, last character = 0).
• All the font association table entries must be in ascending point size order.
• No two font association table entries may reference exactly the same point size and style.
• The offsets to the width table, kerning table and style mapping table must be valid or zero.
• The font ID must not be zero unless it’s actually the system font.
We can’t promise this is every reason the Finder would report a suitcase as damaged (especially given the second step), but this is most of them.
Fonts and the Script Manager
Text M.TE.FontsAndScripts
Written by: John Harvey & Peter Edberg June 1989
This Technical Note describes how the Script Manager uses the font family ID to determine a script code.
The traps _FontScript, _IntlScript, and _Font2Script all use a font family ID to determine the script interface system code that they return. This Note describes the process, the way the Script Manager renumbers the Chicago font for non-Roman systems, and the equation for calculating Script IDs from font family IDs.
On a Roman system the Chicago 'FOND' is numbered zero, but this causes no confusion since Chicago is also the system font. Non-Roman systems must renumber Chicago so that it will not interfere with the mapping of 'FOND' ID = 0 to the correct system 'FOND'. Typically Chicago is renumbered to 16383.
In Inside Macintosh, Volume V-293, The Script Manager, the descriptions of _FontScript, _IntlScript, and _Font2Script state that the current font identification number (e.g., 'FOND' ID) is used to calculate the correct script code. The equation for calculating script codes from 'FOND' IDs is as follows:
script =((FONDid - $4000) DIV 512) + 1
For a specific example, consider the Kyoto font which is one of the fonts included in KanjiTalk. Its 'FOND' ID is 16385. Plugging that value into the equation above, we get: script = ((16385-16384) DIV 512) +1. Which results in a value of one, the script code for the Kanji script system.
Note that this means that script systems other than Roman can only have 512 separate font families. Furthermore, Roman font families (FOND) must not have an ID greater than 16383, and 'FOND' ID 16383 is reserved for Chicago on non-Roman systems.
So How Do They Work?
_FontScript, _IntlScript, and _Font2Script begin by setting two Script Manager globals, Forced and Default to false. Then the two special font family ('FOND') numbers zero and one are mapped to the System and Application font.
Next the 'FOND' ID is tested to see if it is an international font. _FontScript and _IntlScript simply take the value out of the txFont field of the current grafPort. _Font2Script uses the value passed to it. The test is simply:
IF FONDid < $4000 {16384}
script is Roman so return 0
ELSE
script is international so calculate script id using equation described above
Once the script code has been determined, the routine looks at the the Script Manager globals FontForce and IntlForce.
If the currently installed script is Roman and fontForce is true, or if intlForce is true and the routine called was _IntlScript, then the value returned will be the current system script. If the installed script is not Roman; the script code calculated will be returned when the routine called was _IntlScript, intlForce is true, and the script code does not equal the system script.
Once the script code to be returned as been calculated, a final check is made to be sure that the script is installed and enabled. If it is not; Roman is returned, and Forced is set to false and Default is set to true.
What’s This Forced Stuff?
Two Script Manager globals, fontForce and intlForce, are flags that support compatibility. Turning fontForce on will cause Roman fonts to be interpreted as belonging to the system script. This provides compatibility for applications that hard-code font numbers.
For example, the Arabic script interface system provides a cdev which lets a user turn fontForce on. When a user does this, any Roman fonts will be mapped to an Arabic font. Note this is only a partially effective measure since the user still does not have complete control over fonts.
It should also be noted that if a user sets fontForce on via the cdev, values returned for fonts with family IDs in the range $0002 to $3FFF (Roman 'FOND' ID range) may vary. This is not a good feature for applications that allow mixed text. To avoid this problem, an application can turn the fontForce flag off before calling _Font2Script or _FontScript. The flag value should be saved before turning it off, and restored later.
The intlForce flag determines how the call IUGetIntl behaves. If this flag is on, IUGetIntl will always return the international resources ('itlx' where x is 0-2) corresponding to the system script. When intlForce is off, the font in the current port will be used to determine which international resources will be returned. This flag lets an application control what date formats, sorting routines, etc. will be used.
For that reason, before calling any of the international utility routines or using the binary to decimal routines, an application should verify that thePort and thePort^.txFont are set correctly, or that intlForce is set properly.
Let’s Look at a Picture
The flowchart in Figure 1 illustrates the operation of _FontScript, _IntlScript, and _Font2Script, and how they are affected by the global flags fontForce and intlForce.
Figure 1–Operation Flowchart
Further Reference:
• Inside Macintosh, Volume I-493, The International Utilities Package
• Inside Macintosh, Volume V-293, The Script Manager
• Inside Macintosh, Volume V-287, The International Utilities Package
International Resource Q&As
Text M.TX.IntlRsrc.Q&As
Revised by: Developer Support Center October 1992
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As and Q&As revised this month are marked with a bar in the side margin.
Determining the language being used to enter text
Written: 11/20/90
Last reviewed: 8/1/92
Is there a way to tell what language is being used on the Macintosh? I know how to find the script and other international items, but the language being spoken would be a very useful thing to know.
___
It depends on what you mean by “language.” It’s impossible to determine what language the user is typing in without doing a high-level analysis of what is being typed, as you probably know.
One way that’s been suggested in the past is to check on the current KCHR by calling GetScript with an smScriptKeys verb. This returns the resource ID of the currently active KCHR. All international systems come with a U.S. KCHR and possibly others, such as the Romaji and Kana KCHRs in the Japanese systems and the alternative Roman KCHRs in the various German and French systems. The thought was that the user will most likely choose the U.S. KCHR to type in English, and choose the French KCHR to type in French. One problem with this is that it isn’t necessarily true. You can type English with the French KCHR and you can type French with the U.S. KCHR, and many people do. A much larger problem is that the KCHR IDs aren’t standardized within a script system range. Because Apple defines a range of resource IDs for each script system for the international resources, you can tell what script system a KCHR is for, and you can even match up the KCHR ID with the standard KCHR IDs that Apple defines, but there’s nothing wrong with someone creating their own KCHR and giving it any ID they want, as long as it’s in the proper range for the script system they’ve written it for. If that’s the currently active KCHR, its resource ID tells you what script system it’s for, but it won’t tell you anything about what language it was intended for. For this reason, this method is frowned upon now.
In fact, there really is no reliable way of knowing the language that’s being used. The best you can do currently is to find out what the system is localized for. This is done by grabbing the vers resource ID 1 from the System file. In this resource is what was called the country code (the term “country code” is obsolete, in favor of “region code”) which indicates what region the system is localized for. These region codes are defined in Packages.p in MPW, and have names like verUS and verFrance and the like.
There’s another way that you might want to consider. One of the GetScript verbs is smScriptLang. This returns the language code of the specified script system for the current system. If you pass smRoman as the script code to GetScript, it’ll return langFrench on a French system, langGerman on a German system, langEnglish on a U.S. or U.K. or Australian system, and so forth. If you pass smJapanese as the script code to GetScript, it’ll return langJapanese. Interestingly, if you pass smRoman as the script code to GetScript when a Japanese script system is running, it’ll return langEnglish. All non-Roman script systems return langEnglish if you pass smRoman as the script code to GetScript. The language constants are in Language.p in MPW. You’ll probably what to combine this GetScript call with a call to GetEnvirons to find out what the currently active script is. It might look something like this:
(* Get the currently-active keyboard script *)
keyboardScript := GetEnvirons (smKeyScript);
(* Now get the language that the keyboard script corresponds to *)
In summary, there’s no system support for retrieving the language that the user is typing, nor is there any reliable support for retrieving the language related to any KCHR. But, you can find the region code of the active system, and you can find the language associated with the active script. Hopefully, that’s enough information to be useful to you.
RelString & EqualString vs International Utilities
Written: 1/25/91
Last reviewed: 8/1/92
What is the difference between RelString and the EqualString? What does DTS suggest its Macintosh developers use when sorting? Or do you suggest having an option for the international sort?
___
RelString and EqualString are mainly intended for the File Manager. The File Manager uses them for quick-and-dirty string comparison so that it knows how to return files ordered alphabetically when you use an indexed File Manager routines and so that it can detect file name collisions. Beyond that, RelString and EqualString aren’t localizable and they’re not extensible.
The International Utilities string comparison routines are localizable and extensible. They use information in the active 'itl2' resource to determine how the characters are sorted. Most localized systems come with their own 'itl2' resource, and so string comparisons are done correctly for the region that the system is localized for. RelString and EqualString stay the same for all these regions, and so you’ll probably find some cases in which strings are compared incorrectly by these routines.
One important place where RelString and EqualString don’t work very well is with the new characters in the extended Macintosh character set. When the LaserWriter was introduced, the LaserWriter fonts used the extended Macintosh character set which added many new characters, including several new upper-case characters with diacriticals. In system software version 6.0.4, the International Utilities were updated to take advantage of these new characters. For example, the upper case “E” with a grave accent first appeared in the extended Macintosh character set. With 6.0.4, the lower and upper case “E” with a grave accent were considered to be equal in primary ordering, and unequal in secondary ordering, which is correct. But RelString and EqualString, even today, still think in the old Macintosh character set, so they think that lower and upper case “E” with a grave accent have nothing to do with each other, and that’s not right.
Because the RelString and EqualString algorithms are more crude than the string comparison routines in the International Utilities, RelString and EqualString should win races with the International Utilities string comparison routines hands down. The File Manager uses them partly for this reason, and partly because RelString and EqualString don’t need access to the 'itl2' resource, which is usually pretty big. Normally, File Manager sorting isn’t critical anyway because it’s either irrelevant, or it can easily be cleaned up by using the International Utilities string comparison routines. The Standard File package, for example, uses the International Utilities string comparison routines to order file and folder names in its list so that the list is ordered correctly regardless of the system it’s running on.
X-Ref:
Inside Macintosh Volume VI, page 14-82, “The 'itlm' Resource”
Macintosh PRAM’s MachineLocation dlsDelta field
Written: 1/25/91
Last reviewed: 8/1/92
How is the dlsDelta field in PRAM’s time zone MachineLocation record used and set?
___
Currently, the dlsDelta field is not being used by Macintosh system software, nor is its meaning defined. There are plans to use it in the future, so it’s important that you preserve its current value if you ever use WriteLocation to set the value of gmtDelta. See the description of the WriteLocation routine in “WorldWide Development: Guide to System Software,” available on the latest developer CD and from APDA (#M7047/A), for details on getting and setting gmtDelta while leaving dlsDelta intact. In short, the code looks like this:
VAR
myLocation: Location;
myGMTDelta: LongInt;
tempSignedByte: SignedByte;
:
tempSignedByte := myLocation.dlsDelta;
myLocation.gmtDelta := myGMTDelta;
myLocation.dlsDelta := tempSignedByte;
Why 1904 is Macintosh Time base
Written: 9/6/91
Last reviewed: 8/1/92
The global variable Time contains the number of seconds since midnight, Jan. 1, 1904. Why was the year 1904 chosen ?
___
The ability to go back in time is one consideration. You would not want to start the clock from, say, 1984. You also want to go ahead in time a good amount, of course. So, the clock start date needs to put our current date somewhere in the middle of the clock’s range.
So what is the clock’s range? Since the clock chip has a four byte counter which is incremented each second, they had 4,294,967,295 seconds to work with, or approximately 136 years. This would make the Macintosh clock run out in 1904+136 = 2040. The maximum value, $FFFFFFFF, corresponds to 6:28:15 a.m., February 6, 2040.
Given the possible range of years/leap years/nonleap years (every fourth year, but not if at the end of a century, except at the end of every fourth century, which is a leap year) and the date when the clock will run out (2040) - the “leap year code” in the Macintosh only has to deal with the rule “every fourth year is a leap year” because none of the possible Macintosh dates violate that rule! Remember, 2000 is evenly divisible by 400, so it IS a leap year. 1900 is not. If they started at 1900, they would have to use a different algorithm that accounts for “non-leap year” leap years. Some other clocks start on 1901.
Why did they start on 12:00:00, January 1, 1904? Well, it probably has to do with 1904 being the first leap year after a “non-leap year” leap year (1900). So, the year was chosen for mechanical (4 byte limit on number of seconds) and pragmatic (you only want to use one algorithm to figure out the date et al) considerations.
So, back to the future...
X-Ref:
Chapter 4, “Worldwide Guide to System Software”
System 7 and 'itl1' resources
Written: 9/16/91
Last reviewed: 8/1/92
System 7 doesn’t recognize some of my 'itl1' resource changes, such as date abbreviation length, that are recognized by System 6.
___
The field that you refer to is not doing what you want because System 7 introduced an expanded 'itl1' resource, with several new fields, including arrays that contain the proper abbreviations of all the months and days. If you look in Inside Macintosh Volume VI on pages 14-87 thru 14-89 you’ll find a description of this new resource as well as the rez definition of it. Note the two new arrays, abbrevDays and abbrevMonths. If you were to modify these additional fields, you’d see the date in the finder windows change. (In fact, the abbrev length field does not seem to be used if these new fields are present.)
'itl0' resource time1Suff…time8Suff field interpretation
Written: 11/4/91
Last reviewed: 8/1/92
How are the 'itl0' resource time1Suff, time2Suff, … to be interpreted? On the German system, these bytes seem to contain “ Uhr Uhr.” The correct 24-hour time suffix should be a single “ Uhr.” How do we know how many bytes of this 8-character entity are significant? Similar question for the mornStr and eveStr: are all 4 characters of each of these significant?
___
The time1Suff…time8Suff fields in the 'itl0' resource of all localized systems divide those fields into two sections. The first four bytes (time1Suff through time4Suff) are used from 12:00 midnight through one second before noon, and the last four bytes (time5Suff through time8Suff) are used from 12:00 noon through one second before midnight. This is to take into account any system that has hours from 00:00:00 through 23:59:59, but has different time trailers for the morning and evening hours. I don’t know of any that work this way offhand, though. The German standard is just to append “Uhr” onto the time, morning or evening, and it’s separated from the time by one space. Any unused bytes just contain zero. For example, if the German time suffix was “Uh”, then the time suffix fields would contain a space, “U”, “h”, zero, space, “U”, “h”, and zero. If there’s no time suffix, then all eight bytes are zero.
The morning string and evening strings are done in a similar way. Each one holds a maximum of four bytes, and any unused bytes are filled with zeros.
By the way, you’ll find that a lot of time suffixes are filled in with something even though the flags say that time suffixes aren’t used. This is just localization garbage, which probably will be cleaned up someday.
International Canceling
Text M.TE.InternationalCancel
Written by: John Harvey February 1990
This Technical Note describes potential problems canceling operations with the Command-period key sequence and international keyboards.
Where Did That Key Go?
Canceling an operation, from printing to compiling, has always been done with the key sequence Command-period. The problem with this is that on some international systems, one needs to hold the Shift key down to produce a period. Many keyboard mappings, including that of the U.S., ignore the Shift key when the Command key is down. In other words, on a system where a period (.) is a shifted character (e.g., Italian) pressing Command-Shift-KeyThatMakesAPeriod does not generate the ASCII code for a period. Instead, the keyboard mapping software generates the ASCII code for the unshifted character. If an application is looking for Command-period to cancel some time intensive operation, and an international user types the shifted key sequence that normally produces a period along with the Command key, the application is going to miss that request unless it takes special precautions.
A Bit Confusing (to me at least)
The solution to this potential international disaster is to strip the Command key out of the modifiers, and then run the key code back through the keyboard mapping software. The trap _KeyTrans makes this procedure very easy. _KeyTrans takes as parameters a pointer to a 'KCHR' resource (see M.TB.KeyMapping), a word which contains the keycode and the modifier bits, and a word which serves as a state variable.
One note on the result returned by _KeyTrans. Inside Macintosh, Volume V-195, The Toolbox Event Manager, states, “ASCII 1 is the ASCII value of the first character generated by the key code parameter.” This statement is followed by an illustration (Figure 7 on page V-195) which shows ASCII 1 as the low byte of the high word in the long word result. Although this statement and the accompanying illustration are correct, they have mislead a number of people (me for one).
It is dangerous to expect the character code in one particular word of the long word result. In fact, the architecture of the _KeyTrans trap does not specify which word contains the character code in which you might be interested. This is because the _KeyTrans trap’s primary purpose is to create a package that can be used to build a key-down event, and the Toolbox Event Manager just doesn’t care about particular keys. In fact, it is possible to get a result from _KeyTrans that contains character codes in both words. This is how dead keys are handled.
But how does one handle a particular character, specifically a period? The strategy adopted in the sample function in this Note is to check both words of the result. If a period exists in either word and the Command key is down, it is counted as a Command-period key sequence.
Now that everything is straight about parameters and results, it’s time to look at some sample code. The code fragment which follows ensures that you get that period regardless of the state of the modifier keys.
MPW Pascal
CONST
kMaskModifier = $FE00; {need to strip command key from Modifiers}
kMaskVirtualKey = $0000FF00; {get virtual key from event message}
kMaskASCII1 = $00FF0000;
kMaskASCII2= $000000FF; {get key from KeyTrans return}
kKeyUpMask = $0080;
kPeriod = ORD('.');
TYPE
EventPtr = ^EventRecord;
FUNCTION CmdPeriod(theEvent: EventPtr): Boolean;
VAR
keyCode : Integer;
virtualKey,
keyInfo,
lowChar,
highChar,
state,
keyCId : Longint;
hKCHR : Handle;
BEGIN
CmdPeriod := FALSE;
IF ( theEvent^.what = keyDown ) | ( theEvent^.what = autoKey ) THEN BEGIN
{see if the command key is down. If it is, get the ASCII }
IF BAND(theEvent^.modifiers,cmdKey) <> 0 THEN BEGIN
virtualKey := BAND(theEvent^.message,kMaskVirtualKey) DIV 256;
{strip the virtual key by ANDing the modifiers with our mask}
/* Don't bother locking since KeyTrans will never move memory */
keyInfo = KeyTrans(*hKCHR, keyCode, &state);
ReleaseResource( hKCHR );
}
else
keyInfo = (*theEvent).message;
lowChar = keyInfo & kMaskASCII2;
highChar = (keyInfo & kMaskASCII1) >> 16;
if (lowChar == kPeriod || highChar == kPeriod)
fTimeToQuit = true;
} // end the command key is down
} // end key down event
return( fTimeToQuit );
}
What About That Resource
The astute observer may have noticed that the code example requires that you read a resource. Although this certainly isn’t that big of a deal, it is always nice when you can cut down on disk accesses. In System 7.0 a verb is added that can be used to get _GetEnvirons to return a pointer to the current 'KCHR'. The verb is defined and used as follows:
Pascal
CONST
smKCHRCache = 38;
KCHRPtr := GetEnvirons(smKCHRCache);
C
#define smKCHRCache 38
KCHRPtr = GetEnvirons(smKCHRCache);
Unfortunately, in system software prior to 7.0, you must use _GetResource as demonstrated above to obtain the current 'KCHR' resource. However, since _GetEnvirons always returns zero when passed a verb it does not recognize, you can build System 7.0 compatibility into your application without having to check which system software is running. To do this, you could modify the routines as follows:
Pascal
CONST {define our own constant until System 7.0 headers ship. At that point, if you
have not shipped, you can put in the real constant}
NewVerb_smKeyCache = 38;
VAR
KCHRPtr : Ptr;
KCHRPtr := Ptr(GetEnvirons(NewVerb_smKeyCache ));
hKCHR := NIL; {set to NIL before starting}
IF KCHRPtr = NIL THEN BEGIN {we didn't get the ptr from GetEnvirons}
• Inside Macintosh, Volume V, The Toolbox Event Manager
• M.TB.KeyMapping
Changes in International Utilities and Resources
Text M.TE.IUChanges
Revised by: March 1988
Written by: Priscilla Oppenheimer July 1987
The International Utilities package and the international resources have been changed with System file 4.1 to take advantage of the Script Manager.
INTL vs. itl
In the past, there were two INTL resources in the System file, INTL 0 and INTL 1, which contained international formatting options. Starting with System 4.1, itl0 and itl1 replace INTL 0 and INTL 1. (INTL 0 and INTL 1 are still included with the System file so that applications that do a GetResource for them will still find them. Only these erroneous applications that use GetResource will access these resources now; the System will no longer look at them.) There can now be a set of international resources (itls) for each script that is installed. That is, where once there was one resource type (INTL) with IDs 0 and 1 there are now many different resource types (itl0, itl1, itl2, itlb, itlc, KCHR) with distinct resource IDs for the various countries. The U.S. System file uses resource ID 0 for all itl resources.
If your existing application calls IUGetIntl to get the appropriate international resource, you will have no problems under System 4.1. If, however, you call GetResource on INTL 0 or INTL 1 and then modify them, and expect that the System will use the modified resource, you will no longer work correctly. If your application has been released in the past with a modified System file to include your own INTL 0 and/or INTL 1, you may want to release your application with a modified itl0 and/or itl1.
The International Sorting Routine
The international sorting routine is used to handle cases where letters are equal in primary ordering but different in secondary ordering (e.g., “ä” and “a”). The routine can also handle cases where one character sorts as if it were two, or two characters would sort as if they were one, or even character reversals.
Prior to System 4.1, the international sorting routine was stored starting with the localRtn field of INTL 1. As of System 4.1, this routine is now stored in itl2. An INTL 2 resource is also included with the international resources, just for consistency (the System never uses it). It is exactly the same as itl2.
Writing your own sorting routine can be dangerous if it is not done correctly. Before attempting to do this, we would highly recommend you check with Apple to determine if we are already doing one for the language of interest. We plan to do many international versions of System file 4.1, and most developers’ needs will be met by these. A new policy at Apple will now make it possible to license foreign versions of the System file from Software Licensing. Contact Software Licensing at (408) 973-4667 for more information about this.
Error in APDA draft of Inside Macintosh Volume V
There is an error in the APDA draft of the International Utilities Package chapter of Inside Macintosh Volume V. It says that there is a flag in the Script Manager globals that overrides the current font and always uses the INTL 0 and INTL 1 resources. This is not true. INTL 0 and 1 have been replaced by itl0 and itl1, and are not used except by applications that explicitly get them by doing a GetResource. There is a relevant Script Manager flag called IntlForce that is correctly but incompletely explained in the Script Manager chapter. When IntlForce is false, the resources used by the International Utilities are determined by the current script. The script that is in use is determined by which font is in use for the port in use. For example, if you switch to the Kyoto font, then you will be using the Japanese Script Interface System (KanjiTalk). If you do this with IntlForce set to false, then you will use the resources that are associated with the Japanese Script Interface System.
When IntlForce is true, the International Utilities always use the resources that are associated with the system script; this is usually Roman. IntlForce is true by default. Actually, the Script Manager initialization routine reads the itlc resource to determine what the default is, and Apple plans to release the various international System files with this field set to true in the itlc resource. Applications that want to change the value of IntlForce can use the Script Manager call SetEnvirons with the smIntlForce verb. There is a Script Manager call, IntlScript, to find out the current value.
Bug in System 4.1 version of itl1
There is a bug in the itl1 resource in System 4.1. The itl1 resource contains arrays of day and month names, as did the INTL 1 resource. Their formats are:
days ARRAY [1..7] of STRING[15]
months ARRAY[1..12] OF STRING[15]
Every day and month is supposed to be coded as a Pascal string with a maximum of 15 characters, and the actual length in the first byte of the string. In System 4.0 and System 4.1, the contents of each string is always a Pascal string of length 15, with the unused characters set to nulls. In other words, the length byte is set to hex 0F. This will be fixed in a future release of the System file. If it is currently causing your application problems, you can change the lengths with ResEdit, or change them at run time, or use your own itl1 resource and release your application with an installer script. The recommended approach is to wait for a fixed version of the System file from Apple.
Further Reference:
• The International Utilities
• The Script Manager
Keyboard Resource Q&As
Text M.TX.KeybdRes.Q&As
Revised by: Developer Support Center October 1992
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As and Q&As revised this month are marked with a bar in the side margin.
ResEdit and Macintosh Portable 'KCAP' resources
Written: 6/17/91
Last reviewed: 1/27/92
I am trying to use the 'KCAP' resource to draw the Macintosh Portable keyboard layout in our application. I used the ResEdit to check 'KCAP' in both System 7 and the Key Layout file, but the Portable keyboard type is not there.
___
The resource numbers 6 and 7 of 'KCAP' are contained in the ROM of the Portable and some other Macintosh models. Being located in ROM, the resources are not directly viewable with ResEdit. They do, however, exist in the resource chain, so a RGetResource for 'KCAP' number 6 should load and return a pointer to this resource. ROM resources and the resource chain are discussed in the Resource Manager chapters of Inside Macintosh Volumes IV and V.
Using Macintosh KCAP resource to draw a keyboard
Written: 1/10/92
Last reviewed: 4/7/92
How can I use the KCAP resource to draw the keyboard?
___
KCAP resources describe the key layout of Macintosh keyboards. They are intended for use by the Key Caps desk accessory, but applications can also take advantage of KCAPs for information about key arrangements. The program kcapApp demonstrates use of the KCAP resource and is available in the Snippets collection.
This is the format of the KCAP resource:
rect, boundary for the keyboard (8 bytes)
rect, appropriate for an editable line of text (8 bytes)
integer, number of key shapes to follow (2 bytes)
for each key shape:
integer, number of points defining this key shape - 1 (2 bytes)
for each point:
point, opposite corner of a rect (2 bytes)
number of keys of this shape - 1 (2 bytes)
for each key:
byte, modifier mask (1 byte)
byte, OR/AND setting for modifier mask (1 bit) plus
virtual keycode for this key (7 bits)
integer, vertical offset from previous key (2 bytes)
integer, horizontal offset from previous key (2 bytes)
The keyboard boundary rect may be offset from the origin. It can be realigned with
Each key shape consists of one or more rectangles. The rectangles are defined by a series of points, with one point specifying each rectangle. The first point is the opposite corner of a rectangle anchored at the starting pen location for the key; the next point is the opposite corner of a rectangle from the last point; and so on. The key shape is the union of the rects defined by the points.
Note that a rectangle’s point may be above or to the left of the previous point. To QuickDraw, this would define an empty rectangle, so the rectangle’s horizontal and/or vertical coordinates may need to be swapped before FrameRect is called to add a rect to the key’s region.
The pen is moved to the top left of the keyboard boundary rect before drawing each group of keys of a given shape. The pen location for each key is given as a horizontal and vertical offset from the pen location for the previous key. The pen location for the first key of each shape is an offset from the top left corner of the keyboard boundary rectangle. The pen does not change location when a key is drawn.
Use KeyTrans to determine the character to be drawn on the key. The KCAP resource provides a 7-bit virtual keycode, plus a mask for the modifiers to be passed to KeyTrans. If the most significant bit of the keycode byte is set, AND the desired modifiers with the modifier mask. If the bit is clear, OR the modifiers with the mask.
The high bit of the virtual keycode byte indicates to KeyTrans the type of the keystroke; before calling KeyTrans, clear the bit for a keydown, or set it for a keyup. The bit should be clear if you’re drawing a keyboard. KeyTrans sets the state appropriately for dead keys only when called for keydowns.
The global KbdType ($21E), a byte, contains the KCAP resource number for the last keyboard used. Under System 6, KCAP resources are kept in the file “Key Layout” in the System folder. Starting with System 7, KCAP resources are in the System file. Alternatively, a machine’s KCAP may be stored in ROM, where it’s available with the GetResource call but cannot be viewed with ResEdit.
For more information on KeyTrans and KCHRs, see Chapter 10 of Inside Macintosh Volume V, pages 14-23 and 14-96 of Inside Macintosh Volume VI, and the Macintosh Technical Note “Key Mapping.” KCAP resources are discussed on pages 14-95, 14-100 and 14-101 of Inside Macintosh Volume VI.
Macintosh Option key: Techniques for ignoring
Written: 10/16/91
Last reviewed: 2/28/92
I would like to use the Macintosh Option key as the Control key for keyboards that lack a Control key, such as for Macintosh Plus keyboards. How can I find the ASCII value that would have been returned if the Option key weren’t pressed, in a way that would be compatible with other keyboards?
___
There are a couple of clean ways to ignore the Option key. One is to “reprocess” the keyboard event. The fourth byte of the event message is the ASCII code, but the third byte is the virtual key code. The system calls the trap KeyTrans to map the virtual key code to ASCII according to a 'KCHR' resource. You can do the same, and since the high byte of the modifiers are part of the input to KeyTrans, you can strip out the Option key, call KeyTrans on the third byte of the event message, and get back the “true” ASCII keypress. KeyTrans and 'KCHR' resources are documented in Chapter 10 of Inside Macintosh Volume V, Chapter 14 of Inside Macintosh Volume VI (pages 14-23 and 14-96), and the Macintosh Technical Note “Key Mapping.” In Pascal, these steps would look something like:
keycode := BAND(event.message, keyCodeMask); { get virtual code }
The resource ID of the current KCHR is available as GetScript(GetEnvirons(smKeyScript), smScriptKeys). Alternatively, a pointer to the KCHR data is available under System 7 as GetEnvirons(smKCHRCache), as discussed in the Tech Note “International Canceling.”
This method may work for you, but there is a possible problem: dead keys. Since dead keys are processed before your application gets the event, your application will not see (for example) the first Option-e typed. However, the dead keys are specified in the 'KCHR' resource, so you can create a KCHR (ResEdit 2.1.1 includes a template) to omit dead keys (and, if you choose, option characters) and include it with your application. Call SetScript to get the system to use your 'KCHR' (see the Tech Note “Key Mapping,” IM V-313, and IM VI 14-40).
Another problem is that System 7 ignores a 'KCHR' in the application’s resources, so an application’s 'KCHR' has to be installed in the system. You can install the 'KCHR' with an installer program, or provide a keyboard file users can drag to the System file. (To create a keyboard file, use ResEdit to put the 'KCHR' in the System file, and then drag it out with the System 7 Finder.)
Script Manager’s Pixel2Char Routine
Text M.TE.Pixel2Char
Revised by: Mark B. Johnson & Dave McGary August 1989
Written by: Sue Bartalo June 1989
This Technical Note discusses the Pixel2Char routine provided by the Script Manager.
Changes since June 1989: Clarified information, corrected minor errors, and replaced the illustration.
The leftSide flag in the Pixel2Char routine was inappropriately named, and it should now be called the leadingEdge flag. The reason for this change is that the value Pixel2Char returns indicates whether a mouse-down occurred on the leading edge of a character, which is not always the left side. (In Arabic or Hebrew, both of which are right-to-left scripts, the mouse-down occurs on the right side of the character.)
With this change, the interfaces also change. Following are both the old and new definitions in Pascal and C respectively:
Old Definition
FUNCTION Pixel2Char(textBuf: Ptr;textLen: INTEGER;slop: INTEGER;pixelWidth: INTEGER;
VAR leftSide: BOOLEAN): INTEGER;
pascal short Pixel2Char(Ptr textBuf,short textLen,short slop,short pixelWidth,
Boolean *leftSide)
New Definition
FUNCTION Pixel2Char(textBuf: Ptr;textLen: INTEGER;slop: INTEGER;pixelWidth: INTEGER;
VAR leadingEdge: BOOLEAN): INTEGER;
pascal short Pixel2Char(Ptr textBuf,short textLen,short slop,short pixelWidth,
Boolean *leadingEdge)
The value of the leadingEdge flag is True if a mouse-down occurs on the leading edge of the character in its direction (e.g., the left side for a left-to-right script (Roman) and the right side for a right-to-left one (Arabic or Hebrew)). Figure 1 illustrates these differences.
This Note describes the way script systems should work; however, in some systems, the values of leadingEdge and character offset are undefined when the pixelWidth is outside the boundaries of the text.
You define the start of a right-to-left line to be on the right and the end to be on the left; therefore, it follows that the start of a left-to-right line is on the left, while the end is on the right. The values at the start of a line should be False for leadingEdge and zero for character offset. The values at the end are True for leadingEdge and the character offset is the total byte count of that line. You should check for these out-of-bounds conditions explicitly and perform the appropriate actions. This change will appear in the next version of the Script Manager documentation.
Figure 1–Pixel2Char leadingEdge Flag
Accessing the Script Manager Print Action Routine
Text M.TE.PrintAction
Revised by: March 1988
Written by: Mark Davis November 1987
This technical note describes how Print Drivers can access the Script Manager Print Action routine to print unconventional text, such as Japanese or Arabic.
General Notes
Scripts such as Japanese or Arabic modify the normal QuickDraw text handling in order to represent text properly. On the screen, this is done by trapping StdText and StdTxtMeasure, and transforming the text before printing. For example, for Hebrew or Arabic the text might be reversed, since text normally goes from right to left in those scripts.
Print drivers require slightly different handling, for two reasons:
1. A print driver might not call the standard QuickDraw procedures. For example, the LaserWriter writes directly in PostScript instead.
2. A print driver might need to format the text, for accurate line-layout. In this case, the text needs to be transformed before the driver performs line-layout. If the driver is spooling the text, and will replay the text a second time, the text cannot be transformed a second time, since that would ruin the appearance.
For example, the ImageWriter driver calls QuickDraw procedures twice, once to spool and once to unwind the spooling. The text must be transformed when spooling, so that line layout can be done, but when unwinding, the transformation must be turned off completely.
Note that some drivers, such as the LaserWriter, use QuickDraw re-entrantly: the application program calls a QuickDraw routine, which is directed to the driver’s grafProcs, which in turn call QuickDraw internally to put up status messages on the screen. The Print Action procedure handles the text properly so that the text transformations are enabled during the re-entrant calls, so that the status messages will be properly formatted.
When To Call the Print Action Routine
The Script Manager Print Action routine allows the print driver to be independent of the particular scripts being used. The printing driver should call this routine whenever it changes the grafProcs in the printing grafPort. The Print Action routine will then substitute grafProcs of its own in the grafProcs record, saving the original routine addresses.
The Print Action routine will actually call a Print Action routine for each script system that is currently installed. Each of the script Print Action routines will do the appropriate tasks for its system.
Calling the Print Action Routine
To call the Print Action routine, the driver should use the following code:
intlGlobals equ $ba0 ; international globals
printActionOff equ $16 ; offset to PrintAction proc ptr
; get procedure pointer to call
tst.w Rom85 ; on a Macintosh + or better?
blt.s @PrintActionDone ; no, skip
move.l intlGlobals,d2 ; get international globals
ble.s @PrintActionDone ; not there, skip
move.l d2,a0 ; in address register
move.l printActionOff(a0),d2 ; get print action address
beq.s @PrintActionDone ; not there, skip
move.l d2,a0 ; in address register
; set up arguments to call
move.l <myPort>,d0 ; pass the port
move.w <myVerb>,d1 ; pass the verb
jsr (a0) ; call the procedure
@PrintActionDone
Print Action Routine Verbs
There are currently three verbs to pass to the Print Action routine.
paUnwindText equ –1
paSpoolText equ 1
paNoQuickDraw equ 3
Use the paUnwind verb to ensure that the text is not transformed before your StdText procedure receives the text. This verb is used when playing back stored text that has already been transformed.
The other two verbs (paNoQuickDraw and paSpoolText) are used to ensure that the text is transformed before your StdText procedure receives the text. The paSpoolText verb is used when your driver will use QuickDraw to image the text in the printing grafPort. The paNoQuickDraw verb is used when the text is not drawn into the printing port by going through QuickDraw (e.g. the LaserWriter). In that case some languages (e.g. Japanese) which use an extended font structure may need to recast the text calls as CopyBits calls.
As mentioned above, some applications may call QuickDraw from within the driver, as when a status window is updated. During any StdTxtMeasure calls in the driver during the application’s call to StdText, the port is checked against the printer port. If they match, then the text is not transformed. Otherwise, the text is transformed.
The solutions adopted by the Print Action routine assume that the print driver does not measure or draw text except within calls to StdTxtMeasure or StdText. If your driver does text buffering (as for line layout), make sure that any measurements are performed within these two calls. For example, you might buffer both the text and its screen width as measured by QuickDraw.
Further Reference:
• The Script Manager
Safe cdevs
Text M.TE.SafeCDEV
Written by: John Harvey August 1989
This Technical Note describes a potential problem with Control Panel devices (cdevs) that contain EditText fields and presents a way to avoid it.
The Control Panel chapter in Inside Macintosh, Volume 5 describes, in detail, how run-time errors are handled by the Control Panel and a cdev. There is, however, a potential problem with cdevs that contain EditText items that this chapter does not cover.
When a cdev is called by the Control Panel, the cdev’s 'DITL' resource is concatenated to the Control Panel’s 'DITL'. The Control Panel then lets the Dialog Manager update the window. If the cdev contains an item of type EditText, the Dialog Manager allocates and activates a TEHandle to be used for displaying and editing text. All of this action happens before the cdev gets the initDev message from the Control Panel.
As detailed in The Control Panel chapter, if an error occurs from which a cdev cannot recover, the cdev should dispose of any private memory and return the appropriate error code or a NIL value to the Control Panel. The Control Panel then grays out the cdev’s area, displays the appropriate error dialog, and then deletes the items that were added to its 'DITL'.
All of this is fine, except that the TEHandle does not get deallocated. The EditText items get thrown away, including the strings in the item list that the Dialog Manager would use to store text entered into the EditText field, but the TEHandle stays there and stays active. Figure 1 illustrates what this would look like.
Figure 1–Erroneous Insertion Point
So the Dialog Manager, knowing that it allocated a TEHandle for an item that was visible, goes merrily on its way flashing the insertion point. The problem is not simply one of appearance. If a user hits a key, the Dialog Manager tries to process the key-down event just as if the EditText item was still available, and this series of events causes a rather nasty crash.
Fortunately, the solution for this problem is a very simple one. If an EditText item is hidden with a _HideDItem call, the Dialog Manager does not consider it active and will not try to process key-down events for it. So if your cdev contains EditText items, part of your error handling should be to first hide the EditText items with a call to _HideDItem before returning an error code or a NIL as the cdev’s function result.
Further Reference:
• Inside Macintosh, Volume I, The Dialog Manager
• Inside Macintosh, Volume IV, The Dialog Manager
• Inside Macintosh, Volume V, The Control Panel
Script Manager Q&As
Text M.TX.ScriptMgr.Q&As
Revised by: Developer Support Center November 1992
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As and Q&As revised this month are marked with a bar in the side margin.
Using FormatXToStr and FormatStrToX with Pascal switches
Written: 12/10/90
Last reviewed: 8/1/92
Why do the FormatXToStr and FormatStrToX Script Manager routines stop working when I use the Pascal -MC68881 switch?
___
Regular SANE extended numbers are 10 bytes long while MC68881 extended numbers are 12 bytes long, and the extra two bytes are right in the middle of every 68881 extended number. Appendix G “The SANE Library” in the Macintosh Programmer’s Workshop (MPW) Object Pascal version 3.1 manual goes into detail about this. The FormatX2Str and FormatStr2X parse the extended number you pass them directly, and they can only parse 10-byte extended numbers. Fortunately, you can still use the -mc68881 option with these routines as long as you convert any extended numbers to 80-bit extended numbers before passing them to FormatX2Str and FormatStr2X. The SANE.p unit has routines to do this called X96toX80 and X80toX96 (incorrectly documented as X96to80 and X80to96 in the MPW Object Pascal manual). Because the extended80 and extended96 types aren’t equivalent to the extended type as far as Object Pascal is concerned, you have to redeclare FormatX2Str and FormatStr2X to take these types. You can do this as follows:
FUNCTION FormatX2Str80 (x: extended80;
myCanonical: NumFormatString;
partsTable: NumberParts;
VAR outString: Str255): FormatStatus;
INLINE $2F3C,$8210,$FFE8,$A8B5;
FUNCTION FormatStr2X80 (source: Str255;
myCanonical: NumFormatString;
partsTable: NumberParts;
VAR x: extended80): FormatStatus;
INLINE $2F3C,$8210,$FFE6,$A8B5;
Call these routines instead of the originals. To call FormatX2Str80, all you have to do is this:
VAR
x: extended80; {96-bit extended number}
myCanonical: NumFormatString;
partsTable: NumberParts;
outString: Str255
result := FormatX2Str80 (X96toX80 (x), myCanonical, partsTable, outString);
Calling FormatStr2X80 is just slightly more complicated because the extended number is passed by reference:
VAR
x: extended; {96-bit extended number}
x80: extended80; {80-bit extended number}
source: Str255;
myCanonical: NumFormatString;
partsTable: NumberParts;
x80 := X96toX80 (x);
result := FormatStr2X80 (theString, realCanon, PartsTable, x80);
x := X80toX96 (x80);
You should find that these calls now work properly with the -mc68881 option
set. This of course means that you’ll need two versions of the source code; one with the calls to convert between 96-bit and 80-bit extended numbers for use with the -mc68881 option and another one which just uses plain old 80-bit extended numbers for use when the -mc68881 option is turned off.
X-Ref:
Inside Macintosh Volume VI, page 14-49.
String2Date and Date2Secs conversion surprises
Written: 9/17/91
Last reviewed: 8/1/92
String2Date and Date2Secs treat all dates with the year 04 to 10 as 2004 to 2010 instead of 1904 to 1910.
___
This is correct; the Script Manager treats two-digit years less than or equal to 10 as 20xx dates if the current year is between 1990 and 1999, inclusive. Basically, it just assumes that you’re talking about 1-20 years in the future, rather than 80-100 years in the past. The same is true of two-digit 9x dates, when the current year is less than or equal to xx10. Thus, in 2003, the date returned when 3/7/94 is converted will be 1994, not 2094. This is all documented in “Worldwide Development: Guide to System Software,” available from APDA.
FormatX2Str strings
Written: 11/6/91
Last reviewed: 8/1/92
Using the Script Manager to convert numbers to strings and vice versa, in any language, what’s the best way to create the string to pass to FormatX2Str? Will strings using the characters: “#” or “0” or “.” or “,” work no matter what script is currently running, and if not, what can I do?
___
The number format string and canonical number format string mechanisms that you use with FormatX2Str and its kin is a strange design, for exactly the reason that you asked about. The number format string (the one with the characters such as “#” and “0”) does not necessarily work right regardless of the current script. In fact, it doesn’t even necessarily work right between localized versions within one script system. The canonical number format string does work between localized systems and between script systems. The strange thing is there’s an easy way to store number format strings (usually in a 'STR ' resource), but no obvious way to store canonical number format strings. Here’s what you can do when converting between numbers and strings:
When you convert a number format string to a canonical number format string with Str2Format on a U.S. system, it converts it from something like “###.###” to a canonical number format string that looks something like, “three digits, a decimal point, and three digits.” On a German system, that same number format string would be converted to “three digits, a thousands separator, and three digits.”
What you can do to get around this is to save the canonical number format string in a resource instead of the number format string. The canonical string stores things in a language- and script-independent way. Create this resource by writing a trivial utility program that takes a number format string and calls Str2Format to convert it into a canonical number format string, and then copy this into a handle and save it as a resource of a custom type, like 'NUMF'. In your real program, load the 'NUMF' resource, lock it, and then pass the dereferenced handle to FormatX2Str and FormatStr2X.
You can see this done in the ProcDoggie Process Manager sample from the 7.0 Golden Master CD. Take a look at the SetUpProcessInfoItems procedure in UProcessGuts.inc1.p file. You’ll see that the 'NUMF' resource is loaded, locked, and then passed to FormatX2Str. The result is displayed in the Process Information window.
If your program is localized by nonprogrammers, then you might want to provide the utility that converts a number format string to a canonical number format string resource just in case they have to change the entire format of the string. Then they can install the new 'NUMF' (or whatever you choose) resource as part of the localization process.
Code for truncating a multi-byte character string
Written: 1/24/92
Last reviewed: 8/1/92
I create a Macintosh file name from another file name. Since I am adding information to the name, I must make sure that it is within the 31 chars maximum allowed by the operating system. What I need is the equivalent of the TruncText command, except instead of dealing with pixel width, I want the width to be number of characters (31). I can trunc myself, but I’d rather do a proper “smTruncMiddle” and have it nicely internationalized.
___
If you’re going to be adding a set number of bytes to the end of a existing string and you don’t want the localized ellipsis (from the 'itl4' resource) between the truncated string and your bytes, then you can use this routine:
{ This procedure truncates a Pascal string to be of length maxLength or }
{ shorter. It uses the Script Manager charByte function to make sure }
{ the string is not broken in the middle of a multi-byte character. }
VAR
charType: Integer;
BEGIN
IF Length(theString) > maxLength THEN
BEGIN
charType := CharByte(@theString[1], maxLength);
WHILE ((charType < 0) OR (charType > 1)) AND (maxLength <> 0) DO
BEGIN
maxLength := maxLength - 1;
charType := CharByte(@theString[1], maxLength);
END;
theString[0] := chr(maxLength);
END;
END;
If you want the localized ellipsis (from the 'itl4' resource) between the truncated string and your bytes, or you want the localized ellipsis in the middle of the combined strings truncated to a specific length, then you can use this routine:
FUNCTION TruncPString (maxLength: Integer; VAR theString: Str255;
truncWhere: TruncCode): Integer;
{ This function truncates a Pascal String to be of length maxLength or }
{ shorter. It uses the Script Manager TruncString function which adds }
{ the correct tokenEllipsis to the middle or end of the string. See }
{ Inside Macintosh Volume VI, pages 14-59 and14-60 for more info. }
VAR
found: Boolean;
first, midPoint, last: Integer;
tempString: Str255;
whatHappened: Integer;
BEGIN
found := FALSE;
first := 0;
last := TextWidth(@theString[1], 0, Length(theString));
IF Length(theString) > maxLength THEN
BEGIN
WHILE (first <= last) AND NOT found DO
BEGIN
tempString := theString; { tempString gets destroyed every }
TruncPString := whatHappened; { will always be smTruncated }
{ in this case }
END
ELSE
TruncPString := smNotTruncated; { the string wasn't too long }
END;
Character type and subtype values within the Kanji system
Written: 11/17/89
Last reviewed: 8/1/92
What are the values of character type and subtype with the Macintosh Kanji system?
___
For Roman, these are the values of character type:
Punctuation 0
ASCII 1
European 7
For KanjiTalk, the values are the same as Roman, with the addition of:
Katakana 2
Hiragana 3
Kanji 4
Greek 5
Russian (Cyrillic) 6
In Roman, the subtype field is interpreted as:
Normal punctuation 0
Numeric 1
Symbols 2
Blanks 3
The KanjiTalk subtype values are the same as Roman except if the character type is Kanji, in which case the subtype field takes these values:
JIS Level 1 0
JIS Level 2 1
JIS User Character 2
Finally, for KanjiTalk, the character direction field is replaced by the In-ROM field. It is 1 if the character is in the ROM card and 0 otherwise.
Script Manager NMeasureJust documentation fix
Written: 7/21/92
Last reviewed: 9/15/92
The Script Manager’s NMeasureJust call seems to work differently depending on our main script (Roman versus Hebrew, for example). Please take a look at the following code and tell me if I’m doing something wrong:
// Under Hebrew system NMeasureJust acts differently than described in
// Inside Macintosh Volume VI; first entry in charLocs array containes width
// of last character and last entry in charLocs array containes zero.
charLocs[charCount] = charLocs[0];
charLocs[0] = 0;
};
....
}
___
The truth is that Inside Macintosh Volume VI is incorrect, and NMeasureJust is working correctly, even though the results seem to defy any kind of sense. Here’s how NMeasureJust works:
NMeasureJust actually puts into the first entry of the charLocs array the pixel offset from the left end of the text to the leading edge of the first character, regardless of the active script system. For Roman text in a Roman script system, that’s easy: it’s always zero. For Hebrew text in a Hebrew script system, the leading edge of the first character is on the right edge of the text, so NMeasureJust puts the pixel distance between the left edge of the text to the right edge of the text——the same thing as the entire width of the text. Each successive entry in the charLocs array is smaller than the previous entry because we’re gradually moving to the left edge of the text as we move through each successive character. The trailing edge of the last character is at the left edge of the string, and so the last entry in the charLocs array is zero.
What if you pass in text that contains both Roman and Hebrew text? This text contains a direction boundary, and NMeasureJust can sense this. For this case, NMeasureJust puts into the charLocs array a series of decreasing values for the right-to-left characters, and a series of increasing values for the left-to-right characters.
You’re probably passing Roman text (ASCII codes less than 127) to NMeasureJust with the Hebrew script system active to get the results that you reported. This case includes two direction boundaries: one at the beginning that’s right-to-left and contains no characters, one that’s left-to-right and contains all your Roman characters, and finally one that’s right-to-left and contains no characters. The first entry in the charLocs array measures the first right-to-left direction run even though it contains no characters, and so it contains the position of the right edge of your text. The last entry in the charLocs array is zero because it measures the last right-to-left direction run, which again contains no characters. All the charLocs entries between the first and the last measure the positions of the left-to-right, Roman characters, and so you get a series of increasing values. This is just a special case of passing text that contains direction boundaries to NMeasureJust.
Interfacing a Macintosh application with Map CDEV data
Written: 8/17/92
Last reviewed: 10/11/92
How can I provide my users with a “hook” to access the geographical database in Apple’s “Map” Control Panel from my application?
___
There’s no supported way of accessing the geographical database contained in the “Map” Control Panel. Here are some hints, however (just to satisfy your curiosity):
The data are stored in a resource of type 'CTY#', ID=-4064, in the Map cdev. The resource format is a list of word aligned (variable length) city entries, preceded by an integer indicating the number of entries. Each entry has the format
[Integer] length in bytes of the entry
[Longint] latitude in Fract; north = +
[Longint] longitude in Fract; east = +
[Longint] GMT difference in seconds; east = +
[Longint] (reserved; set to 0)
[PascalString] name of the city.
Script Manager Variables
Text M.TE.ScriptVars
Written by: John Harvey & Peter Edberg June 1989
This Technical Note describes, in detail, the local and global script variables.
Introduction
The Script Manager maintains a number of global variables which can be read with the routine _GetEnvirons. These variables can be set by a corresponding routine, _SetEnvirons. In addition, each script interface system maintains variables of its own. These are referred to as local variables in Inside Macintosh, Volume V-293, The Script Manager, and are read by _GetScript and set by _SetScript.
Think of it like this: the Script Manager maintains an environment in which different script interfaces can run. The global variables are used to set up and maintain the environment (thus the names for the routines _GetEnvirons and _SetEnvirons), and the local variables control how the script itself works (so we have _GetScript and _SetScript).
Global Variables
When you call _GetEnvirons or _GetScript, you describe the variable you are interested in with a verb. A verb is simply an integer constant which the Script Manager uses to figure out which variable you want to read or set. The z in Inside Macintosh, V-313, gives incorrect names and descriptions for some of the _GetEnvirons and _SetEnvirons verbs. Table 1 provides correct descriptions.
Constant Value Meaning
smVersion 0 Script Manager version number
smMunged 2 Global modification count
smEnabled 4 Script count; 0 if Script Manager not
enabled
smBidirect 6 Bidirectional script flag
smFontForce 8 Force font flag
smIntlForce 10 Force international utilities flag
smForced 12 Current script forced to system script
smDefault 14 Current script defaulted to Roman
script
smPrint 16 Print action vector
smSysScript 18 Preferred system script
smLastScript 20 Last keyboard script
smKeyScript 22 Keyboard script
smSysRef 24 System folder volRefNum
smKeyCache 26 [Obsolete, do not use]
smKeySwap 28 Keyboard swapping resource handle
smGenFlags 30 General flags
smOverride 32 Script override flags
smCharPortion 34 Ch vs Sp Extra proportion, 4.12 fixed
Table 1–Verbs for _GetEnvirons and _SetEnvirons
The descriptions in the table are still a bit sketchy. The next section describes each variable in more detail and describes the size of each global.
Byte or word globals are mapped to the low-order byte or word of the LongInt returned by _GetEnvirons, with the high-order parts set to zero. Similarly, for these globals _SetEnvirons ignores all but the appropriate part (low-order byte or word) of its params value.
Verb Name Bytes Brief Description
smVersion 2 Script Manager version number
At boot time, the version global is initialized to the value SMgrVers. The high byte is the major version number and is defined in the MPW interface files. The low byte is updated when any changes are made to the Script Manager.
smMunged 2 Global modification count
The munged global is initialized to zero at boot time and incremented when:
• _KeyScript changes the key script and updates smKeyScript and smLastScript
• _SetEnvirons is used to change a Script Manager global
smEnabled 1 Script count; 0 if Script Manager not enabled
At boot time or switch-launch time, the enabled global is initialized to zero, then incremented for each script that is installed and enabled. Since the Roman script system should always be installed by the Script Manager, a value of zero indicates that the Script Manager is not enabled.
It should be noted that older versions of the Script Manager treated this as a Boolean. In other words, if there was more than one script installed, _GetEnvirons(smEnabled) would return 255 (when _GetEnvirons returns a Boolean value $FF represents true).
For this reason, when testing to see if more than one script is installed, it is best to test as follows:
scriptsinstalled := GetEnvirons(smEnabled);
IF scriptsinstalled > 1 THEN
{more than one script available, use Chartype, etc.}
smBidirect 1 Bidirectional script flag
The bidirectional global indicates that at least one bidirectional script is installed. It should be set to true ($FF) by the Arabic and Hebrew script systems. This is not presently done, but will be corrected in future versions of these systems.
smFontForce 1 Force font flag
smIntlForce 1 Force international utilities flag
smForced 1 Current script forced to system script
smDefault 1 Current script defaulted to Roman script
At boot time, FontForce and IntlForce are set from the 'itlc' resource, and Forced and Default are set to zero. These are all flags with the value zero for false and $FF for true. FontForce and IntlForce control the operation of the _FontScript, _Font2Script, and _IntlScript routines. Forced and Default report the actions of these routines.
Setting FontForce to true forces Roman fonts to be interpreted as belonging to the system script. This is for compatibility with applications that hard-code font numbers.
IntlForce determines the behavior of the _IUGetIntl call. When intlforce is set to true, _IUGetIntl will return a handle to the international resources (of type 'itlx' where x is 0-2) for the system script. When IntlForce is false, the _IUGetIntl will use the font of the current port to determine the appropriate resources to fetch. Thus date formats, sorting, etc. can reflect the current script.
smPrint 4 Print action vector
Print action routine vector; set up at boot time. See M.TE.PrintAction.
smSysScript 2 Preferred system script
smLastScript 2 Last keyboard script
smKeyScript 2 Keyboard script
At boot time and switch-launch time, SysScript and KeyScript are set from the SysScript field of the 'itlc' resource if that script is installed and enabled; otherwise, SysScript and KeyScript are set to Roman (without setting Default).
The KeyScript global is the current keyboard script, tested and updated by the _KeyScript routine. When _KeyScript changes KeyScript, it moves the old value to LastScript. _KeyScript can also swap the current key script with the last one, which it retrieves from LastScript. The KeyScript value is also used to get the proper keyboard script icon and to retrieve the proper 'KCHR'.
SysScript specifies the system script, and is used, for example, by _FontScript, _Font2Script, and _IntlScript.
KeyScript, LastScript, and SysScript always contain integers that correspond to a script number. Script numbers are documented in The Script Manager chapter of Inside Macintosh, Volume V-293.
smSysRef 2 System folder volRefNum
Set from the global BootDrive at boot time and switch-launch time. SysRef was originally a way of testing for vanilla launch versus switch launch; now the Enabled global is used for that purpose.
smKeyCache 2 [Obsolete, do not use]
smKeySwap 4 Keyboard swapping resource handle
The 'KSWP' resource handle is put here at boot time and switch-launch time. A 'KSWP' resource contains a table of key sequences that will cause the currently installed 'KCHR' (keyboard mapping table) to change to the preferred system 'KCHR', switch to the Roman 'KCHR', or rotate among the available 'KCHR' resources. The table includes the virtual key code and the modifier keys. The following is the 'KSWP' resource for the Kanji script interface system.
The resource says rotate 'KCHR' resources if a Space–Command key occurs, switch to the system 'KCHR' on keypad plus (+)–Command key, and switch to the Roman 'KCHR' on keypad asterisk (*)–Command key.
smGenFlags 4 General flags
Only the two high-order bits are defined (in the file ScriptEqu.a), as follows:
smfShowIcon = 31 (show icon even if only one script)
smfDualCaret = 30 (use dual caret for mixed direction text)
The high-order byte of smgrGenFlags, containing these flags, should be setup from the flags byte in the 'itlc' resource. This is not presently done, but will be fixed in future versions of the Script Manager.
The following MPW Pascal procedure demonstrates how to get script 'SICN' resources to display even if there is only one script system installed.
PROCEDURE SetSICN;
VAR
SICNstate: Longint;
err: Oserr;
BEGIN
SICNstate := GetEnvirons(smGenFlags);
BSET(SICNstate,smfShowIcon);
err := SetEnvirons(smGenFlags,SICNstate);
END;
smOverride 4 Script override flags
At present, this is not set or used by the Script Manager. It is, however, reserved for future improvements.
smCharPortion 2 Ch vs Sp Extra proportion, 4.12 fixed
This is 16-bit fixed-point value in 4.12 format (e.g., 10% = $0199). It is initialized to 10 percent at boot time. It is intended to be used by script systems to allocate space among intercharacter spacing and interword spacing when justifying text.
A 16-bit fixed-point value in 4.12 format is similar to the fixed-point number type defined on page I-79 of Inside Macintosh . The obvious difference being that it is only 16 bits long. The integer part of the value is stored in the high four bits, and the fractional part is stored in the low 12 bits.
16-bit Fixed-Point Number in 4.12 format
Local Variables
Every script interface system has local variables. Page V-132 of Inside Macintosh lists verbs which are constants that indicate which variable you want to read or set. The table of constants used to access the local variable, although more accurate than the global table, does contain a few inaccuracies. In addition four new constants have been added. Table 2 gives the correct constants.
Here again the descriptions are a little terse. The following section describes each variable in more detail and describes the size of each variable.
Verb Name Bytes Brief Description
smScriptVersion 4 Script Interface version number
When the script interface is loaded, this is set to the current version number.
smScriptMunged 2 Local modification count
This variable is incremented each time _SetScript is called.
smScriptEnabled 1 Script Enabled Flag
A Boolean which indicates whether the script has been enabled. Set to $FF when enabled and zero when not enabled.
smScriptRight 1 Right to Left Flag
A Boolean indicating if text should be drawn right to left or left to right. It is set to $FF for right to left text (Arabic and Hebrew scripts) and zero for left to right (Roman).
smScriptJust 1 Justification Flag
A byte flag which describes how text should be justified. The possible settings correspond to the justification flags used by TextEdit.
0 = left justification
1 = center justified
-1 = right justified
smScriptRedraw 1 Word Redraw Flag
A byte flag describing how much of a line should be redrawn when text is being entered.
0 Only draw a character
1 Redraw the entire word
-1 Redraw the entire line (Arabic)
smScriptSysFond 2 Preferred System Font
This is the font family ID for the preferred System Font. In a Roman system, ScriptSysFond is 0, the family ID for Chicago.
smScriptAppFond 2 Preferred Application Font
Font family ID for the preferred Application Font. In a Roman system, ScriptAppFond is 3, the family ID for Geneva.
smScriptNumber 4 Script 'itl0' ID
Resource ID of 'itl0' for this script. The 'itl0' resource describes how numbers and times should be displayed. The resource ID should match the country version code for a given country.
smScriptDate 4 Script 'itl1' ID
Resource ID of the 'itl1' for this script. The 'itl1' describes how dates should be displayed.
smScriptSort 4 Script 'itl2' ID
Resource ID of the 'itl2' for this script. The 'itl2' contains routines for sorting. See M.TE.NewStringComp.
smScriptFlags 2 Script flags
This verb provides access to the script flags word, which contains bit flags that describe features of the script. This word is initialized from the script’s 'itlb' resource. Constants specifying the bit numbers are described in Table 3.
Bit
Constant Number Description
smsfIntellCP 0 script has intelligent cut and paste
smsfSingByte 1 script has only single bytes
smsfNatCase 2 native characters have upper and lower case
smsfContext 3 contextual script (e.g., AIS-based)
smsfNoForceFont 4 will not force characters
smsfB0Digits 5 has alternative digits in B0-B9
smsfForms 13 uses contextual forms for letters
smsfLigatures 14 uses contextual ligatures
smsfReverse 15 reverses native text, right-left
Table 3–Constant Bit Numbers
smScriptToken 2 Script 'itl2' ID
Resource ID of the 'itl4' for this script. The 'itl4' contains contains tables needed by the number formatting and conversion routines and the _intlTokenize routine. See Script Manager 2.0, Interim Chapter.
smScriptRsvd 4 Reserved
smScriptLang 2 Script’s language code
This verb accesses a word which contains the current language code for the script. The language codes are defined in the MPW interface files.
smScriptNumDate 2 Number and date representation codes
This verb accesses a word containing the number and date representation codes for the script. The number representation code is in the high byte of the word, and the date code is in the low byte.
The possible values for number representations and date codes are declared as constants in the MPW interface files.
The number codes are: and the date codes are:
intWestern = 0; calGregorian = 0;
intArabic = 1; calArabicCivil = 1;
intRoman = 2; calArabicLunar = 2;
intJapanese = 3; calJapanese = 3;
intEuropean = 4; calJewish = 4;
calCoptic = 5;
smScriptKeys 4 Script 'KCHR' ID
Resource ID of preferred 'KCHR' resource. The 'KCHR' resource is used to map virtual key codes into the correct character code. See M.TB.KeyMapping.
smScriptIcon 4 Script 'SICN' ID
Resource ID of the small icon that is used to represent which country specific resources ('itl0', 'itl1', 'itl2', 'KCHR') are currently installed in the system. Presently, the Roman system does not display the 'SICN'. Arabic, Kanji, Chinese, and Hebrew interface systems do display this icon in the upper-right corner of the menu bar.
smScriptPrint 4 Script printer action routine
Print action routine vector; setup when script is installed by Script Manager. See M.TE.PrintAction.
smScriptTrap 4 Trap entry pointer
Pointer to Script dispatch routine. Script Manager routines always belong to one of two groups. The first group of routines are common to every script interface system, and the second group must be supplied by the script interface system. This variable will point to a dispatch routine for the interface-supplied routines. When you call _ScriptUtil, it looks at the selector that is passed and either calls a common routine or calls the routine whose address is stored in ScriptTrap. The routine in smScriptTrap will then use the selector to vector to the correct routine. In general, routines that display or measure text in some way will be supplied by the interface.
A list at the end of this Note indicates which routines are implemented by the Script Manager and which routines are supplied by a script interface system.
smScriptCreator 4 Script file creator
The four character creator type for the script interface’s file. For Roman it is “ZSYS,” the same creator as any system file has.
smScriptFile 4 Script file name
A pointer to the a Pascal string which contains the name of the file containing the script interface system. For the Roman SIS, it is System.
smScriptName 44 Script name
A pointer to a Pascal string which contains the script interface’s name. For Roman it is naturally, “Roman.”
Who Does What?
Table 4 breaks the documented routines into common Script Manager routines and interface specific routines.
Common Routines Interface Supplied
_FontScript _CharByte
_IntlScript _CharType
_KeyScript _Pixel2Char
_GetEnvirons _Char2Pixel
_SetEnvirons _Transliterate
_Font2Script _FindWord
_Format2Str _HiliteText
_FormatStr2X _DrawJust
_FormaX2Str _MeasureJust
_GetFormatOrder _ParseTable
_InitDateCache _VisibleLength
_IntlTokenize _FindScriptRun
_LongDate2Secs _PortionText
_LongSecs2Date
_Str2Format
_String2Date
_String2Time
_StyledLineBreak
_ToggleDate
Table 4–Script Manager Routines and Interface Specific Routines
_GetScript and _SetScript, which return the values of local script variables, are implemented by the Script Manager for some verbs and the script interface system for others.
There is also a group of Script Manager routines which don’t use the _ScriptUtil trap, but are documented in The Script Manager chapter of Inside Macintosh, Volume V-293 or The Script Manager 2.0 Interim Chapter. There routines are utilities that read and write to low-memory or PRAM. It is important to use these routines when they are available. That will allow Apple to modify where global variables, etc. are stored, and your application will remain compatible. The utilities are:
GetDefFontSize
GetSysFont
GetAppFont
GetMBarHeight
GetSysJust
SetSysJust
ReadLocation (documented in Interim Chapter)
WriteLocation (documented in Interim Chapter)
Further Reference:
• Inside Macintosh, Volume V-293, The Script Manager
• M.TB.KeyMapping
• M.TE.PrintAction
Modifying the Standard String Comparison
Text M.TE.NewStringComp
Revised by: March 1988
Written by: Mark Davis
Priscilla Oppenheimer November 1987
This technical note describes how to modify the standard string comparison by constructing an itl2 resource. Developers may want to modify the standard string comparison if Apple’s comparison doesn’t meet their needs or if Apple has not written a string comparison routine for the language that concerns them.
General Structure
The itl2 resource contains a number of procedures that are used for accurate comparison of text by the International Utilities Package. Refer to Inside Macintosh, volume V for an explanation of the algorithm used. The default itl2 for standard English text, which does no special processing, has the following form:
The comparison hook procedures are all assembly language based, with arguments described below. Since the routines may be called once per character in both strings, the routines should be as fast as possible.
The condition codes are used to return information about the status of the hook routine. Typically the normal processing of characters will be skipped if the CCR is set to NE, so the default return should always have EQ set. Each of these routines has access to the stack frame (A6) used in the comparison routine, which has the following form:
IUSortFrame Record {oldA6},Decrement
result ds.w 1
argTop equ *
aStrText ds.l 1
bStrText ds.l 1
aStrLen ds.w 1
bStrLen ds.w 1
argSize equ argTop-*
return ds.l 1
oldA6 ds.l 1
aInfo ds IUStrData
bInfo ds IUStrData
wantMag ds.b 1 ; 1-MagStrig 0-MagIdString.
weakEq ds.b 1 ; Signals at most weak equality
msLock ds.b 1 ; high byte of master ptr.
weakMag ds.b 1 ; -1 weak, 1 strong compare
supStorage ds.b 18 ; extra storage.
localSize equ * ; frame size.
EndR
There are three fields in this frame that are of interest for altering text comparison. The supStorage field is an area reserved for use by the comparison hook procedures as they see fit. The aInfo and bInfo records contain information about the current byte positions in the two compared strings A and B, and information about the status of current characters in those string. The IUStrData record has the following form:
IUStrData Record 0
curChar ds.w 1 ; current character.
mapChar ds.w 1 ; projected character.
decChar ds.w 1 ; decision char for weak equality
bufChar ds.b 1 ; buffer for expansion.
justAfter ds.b 1 ; boolean for AE vs ligature-AE.
ignChar ds.b 1 ; flag: ignore char.
noFetch ds.b 1 ; flag: no fetch of next.
strCnt ds.w 1 ; length word.
strPtr ds.l 1 ; current ptr to string.
EndR
The Init Procedure
The Init Procedure is used to initialize the comparison process. The main use for this procedure is for double-byte scripts. As an optimization, the International Utilities will perform an initial check on the two strings, comparing for simple byte-to-byte equality. Thus any common initial substrings are checked before the Init procedure is called. The string pointers and lengths in the IUStrData records have been updated to point just past the common substrings.
Languages such as Japanese or Yugoslavian, which may consider two bytes to be one character, may have to back up one byte, as shown below.
; Note: this should also check for single-byte nigori or maru, as below
InitProc
move.w AStrLen(a6), d0 ; A length
sub.w AInfo.StrCnt(a6),d0 ; see if its changed
beq.s @FixB ; A is done if not
sub.l #2,sp ; return param
move.l AStrText(a6),-(sp) ; textBuf
move.w d0,-(sp) ; textOffset
_CharByte
tst.w (sp)+ ; on character boundary?
ble.s @FixB ; yes, continue
sub.l #1,AInfo.StrPtr(A6) ; adjust pointer
add.w #1,AInfo.StrCnt(A6) ; adjust count
@FixB
move.w BStrLen(a6), d0 ; B length
sub.w BInfo.StrCnt(a6),d0 ; see if its changed
beq.s Quit Init ; B is done if not
sub.l #2,sp ; return param
move.l BStrText(a6), -(sp) ; textBuf
move.w d0, -(sp) ; textOffset
_CharByte
tst.w (sp)+ ; on character boundary?
ble.w @QuitInit ; yes, continue
sub.l #1,BInfo.StrPtr(A6) ; adjust pointer
add.w #1,BInfo.StrCnt(A6) ; adjust count
@QuitInit
bra.s ReturnEQ ; return to the caller.
EndWith
The Fetch Procedure
The Fetch Procedure is used to fetch a character from a string, updating the pointer and length to reflect the remainder of the string. For example, the following code changes the text comparison for Yugoslavian:
; if we have a double-byte char, add the second byte
lea CurChar(a2),a0 ; pass pointer
move.w d4,(a0) ; set value at ptr
clr.w d0 ; pass length
sub.l #2,SP ; allocate return
move.l a0,-(sp) ; pointer
move.w d0,-(sp) ; offset
_CharByte
tst.w (sp)+ ; test return
bmi.s @DoubleByte ; skip if high byte (first two)
; we don’t have a double byte, but two special cases combine second bytes
move.b (a3),d0 ; get next byte
cmp.b #$DE,d0 ; nigori?
beq.s @DoubleByte ; add in
cmp.b #$DF,d0 ; maru?
bne.s ReturnEq ; exit: single byte
@DoubleByte
move.b (a3)+,d4 ; get next byte
subq.w #1,StrCnt(A2) ; dec string length
addx.w d5,d5 ; set x=1 if string len = 0
rts ; return to caller with CCR=NE
The Project Procedure
The Project Procedure is used to find the primary ordering for a character. This routine will map characters that differ only in the secondary ordering onto a single character, typically the unmodified, uppercase character. For example, the following changes the comparison order for some Norwegian characters, so that they occur after ‘Z.’
; Table contains entries of the form r1, r2, o1, o2,
; where r1,r2 are the replacement word, and
; o1, o2 are the original character.
; The entries are sorted by o1,o2 for use in the above algorithm
DC.B 'Z', 3, 'Å', 0 ; Å after Ø
DC.B 'Z', 3, 'å', 0 ; å after Ø
DC.B 'Z', 1, 'Æ', 0 ; Æ after Z
DC.B 'Z', 2, 'Ø', 0 ; Ø after Æ
DC.B 'Z', 1, 'æ', 0 ; æ after Z
DC.B 'Z', 2, 'ø', 0 ; ø after Æ
DC.L $FFFFFFFF ; table end
The Project procedure can also be used to undo the effects of the normal projection. For example, suppose that “œ” is not to be expanded into “oe”: in that case, a simple test can be made against 'œ',0, returning NE if there is a match, so that the normal processing is not done. To expand one character into two, the routine should return the first replacement character in D4.W, and modify two fields in the IUStrData field. For example, given that A1 points to a table entry of the form (primaryCharacter: Word; secondaryCharacters: Word), the following code could be used:
…
move.w (a1)+,d4 ; return first, primary character
move.w (a1)+,CurChar(A2) ; original => first, modified char.
addq.b #1,JustAfter(A2) ; set to one (otherwise zero)
move.b (a1),BufChar(A2) ; store second character (BYTE!)
…
CurChar is where the original character returned by FetchChar is stored. If characters are different even after being projected onto their respective primary characters, then the CurChar values for each string will be compared. JustAfter indicates that the expanded character should sort after the corresponding unexpanded form. This field must be set whenever CurChar is modified in order for the comparison to be fully ordered. BufChar stores the next byte to be retrieved from the string by FetchChar.
To handle the case where characters are ignored unless the two compared strings are otherwise equal, the IgnChar flag can be set. This can be used to handle characters such as the hyphen in English, or vowels in Arabic.
…
cmp.w #hyphen,d0 ; is it a ignorable?
seq IgnChar(a2) ; set whether or not
…
The Vernier Procedure
The Vernier Procedure is used to make a final comparison among characters that have the same primary ordering. It is only needed if the CurChar values are not ordered properly. For example, according to the binary encoding, å < Ã. To change this ordering so that uppercase letters are before lowercase letters, Ã is mapped to $7F in normal comparison. Notice that only the characters in the secondary ordering are affected: Ã can be mapped onto Z, but not onto Ä, since that would cause a collision.
To write an itl2 resource, follow the guidelines in M.PT.StandAloneCode for writing standalone code in MPW. The code should be written in assembly language, and it must follow the specifications given in this technical note or serious system errors could occur whenever string comparisons are made.
The default comparison routine is in the itl2 resource of the System file. In order to use a comparison routine other than the standard one, you should include an itl2 resource in your application with the same name and resource ID as the one in the System file that you wish to change. The Resource Manager will look for the resource in the application resource file before it looks in the System resource file, so your string comparison routine will be used instead of the default one.
It is generally a dangerous practice to change a system resource since other applications may depend on it, but if you have good reasons to permanently change the system itl2 resource so that all applications use a different comparison routine, then you should write an installer script to change the itl2 resource in the System resource file. Writing an installer script is documented in M.TP.Installer. You are required to write an installer script if you are planning to ship your application on a licensed system software disk and your application makes a permanent change to any resources in the System file. We strongly discourage changing the System itl2 as that would change the behavior of string comparison and sorting for all applications. If that is your intent, then you should write an installer script. However, if you are changing the itl2 resource in the System file for academic or internal use, then you can use a resource editor such as ResEdit to copy your itl2 resource into the System file.
Further Reference:
• The International Utilities
• M.TP.Installer
• M.PT.StandAloneCode
Styled TextEdit Changes in System 6.0
Text M.TE.TextEditChanges
Revised by: Chris Derossi December 1988
Written by: Chris Derossi August 1988
Some changes were made to TextEdit in System 6.0 to provide more functionality and to make life easier for the programmer using TextEdit. This Note documents those changes and enhancements.
Changes since August 1988: Corrected an error in TEDispatchRec in the figure on page 8.
TextEdit Changes
In order to improve the usability of styled TextEdit, some routines have been changed, and some new routines have been added. These changes exist in System Software 6.0 and later. If you intend to rely on any of these changes or new routines, it is important that you call _SysEnvirons first to make sure you are running under System Software 6.0 or later.
_SysEnvirons is documented in Inside Macintosh, Volume V and M.OV.GestaltSysenvirons. To check for the styled TextEdit changes, you might do the following:
VAR
theWorld: SysEnvRec;
anErr : OSErr;
BEGIN
anErr := SysEnvirons(1, theWorld);
IF (anErr = noErr) AND (theWorld.systemVersion >= $0600) THEN …
{System 6.0 or later}
END;
Changes to Existing Routines
_TEKey and _TEDelete have been changed so that backspacing to the beginning of a style no longer deletes that style. Instead, the style is saved in the nullScrap to be applied to subsequently typed characters. As soon as the user has backspaced past the beginning of the style, or clicked in some other area of the text, the style is removed.
GetStylScrap now returns a handle to a valid style scrap record when called for an insertion point (selStart = selEnd). NIL is still returned when GetStylScrap is called with an old style TEHandle.
TESetStyle now accepts an additional mode, doToggle (= 32). When doToggle is specified along with doFace, TESetStyle operates as follows: If a style specified in the given TextStyle parameter exists across the entire selected range, that style is removed (turned off). Otherwise, all of the selected text is set to include that style. When a particular style is set for an entire selection range, that style is said to be continuous over the selection.
For example, given that the following text is the current selection:
then the style bold is continuous over the selection range and the italic style is not. If TESetStyle were called with a mode of doFace + doToggle and a TextStyle tsFace field of [bold], then the resulting selection would be:
On the other hand, if TESetStyle had been called with a mode of doFace + doToggle and a TextStyle tsFace field of [italic], then the selected text would have become:
New TextEdit Routines
Some new routines have been added to TextEdit, TEContinuousStyle, SetStylScrap, TECustomHook, and TENumStyles. These routines are described in detail below.
Assembly language note:
The new TextEdit routines are called via the _TEDispatch trap. Following are the decimal selectors for the new routines:
TEContinuousStyle 10
SetStylScrap 11
TECustomHook 12
TENumStyles 13
TEContinuousStyle
FUNCTION TEContinuousStyle(VAR mode : Integer; VAR aStyle : TextStyle;
hTE : TEHandle) : Boolean;
TEContinuousStyle gives you information about the attributes of the current selection. The mode parameter, which takes the same values as in TESetStyle, specifies which attributes should be checked. When TEContinuousStyle returns, the mode parameter indicates which of the checked attributes is continuous over the selection range and the aStyle parameter is set to reflect the continuous attributes.
TEContinuousStyle returns TRUE if all of the attributes to be checked are continuous and FALSE if not. In other words, if the mode parameter is the same before and after the call, then TEContinuousStyle returns TRUE.
For example, TEContinuousStyle is useful for marking the style menu items based on the current selection.
mode := doFace;
IF TEContinuousStyle(mode, aStyle, myTE) THEN BEGIN
{ There is at least one face that is continuous over the
selection. Note that it might be plain which is actually
CheckItem(styleMenu, boldItem, bold IN aStyle.tsFace);
CheckItem(styleMenu, italicItem, italic IN aStyle.tsFace);
...etc.
END ELSE BEGIN
{ No text face is common to the entire selection. }
CheckItem(styleMenu, plainItem, FALSE);
CheckItem(styleMenu, boldItem, FALSE);
CheckItem(styleMenu, italicItem, FALSE);
...etc.
END;
This function can also be used to determine the actual values for those attributes that are continuous for the selection. Note that a field in the TextStyle record is only valid if the corresponding bit is set in the mode variable; otherwise the field contains garbage. For example, to determine the font, face, size, and color of the current selection:
{ aStyle.tsFace contains the text faces (or plain) that are
common to the selection. }
ELSE
{ No text face is common to the entire selection. };
IF BitAnd(mode, doSize) <> 0 THEN
{ Size for selection = aStyle.tsSize. }
ELSE
{ More than one size in selection. };
IF BitAnd(mode, doColor) <> 0 THEN
{ Color for selection = aStyle.tsColor. }
ELSE
{ More than one color in selection. };
The aStyle.tsFace field is a bit tricky. When TEContinuousStyle returns a mode that contains doFace, and an aStyle.tsFace field that contains [bold, italic], it means that the selected text is all bold and all italic, but may contain other text faces as well. None of the other faces will apply to all of the selected text, or they would have been included in the tsFace field. But if the tsFace field is the empty set ([] = plain), then all of the selected text is plain.
If the current selection range is an insertion point, TEContinuousStyle returns the style information for the next character to be typed. TEContinuousStyle will always return TRUE in this case, and each field of the TextStyle record will be set if the corresponding bit in the mode parameter was set.
SetStylScrap performs the opposite function of GetStylScrap. The newStyles parameter is a handle to a style scrap record which will be applied over the given range of text. The current selection range is not changed. If newStyles is NIL or hTE is a handle to an old style TERecord, SetStylScrap does nothing.
SetStylScrap will terminate without error if it prematurely reaches the end of the range or if there are not enough scrap style elements to cover the whole range. In the latter case, the last style in the scrap record will be applied to the remainder of the range.
TENumStyles
FUNCTION TENumStyles(rangeStart, rangeEnd : LongInt;
hTE : TEHandle) : LongInt;
This function returns the number of style changes contained in the given range, counting one for the start of the range. Note that this does not necessarily represent the number of unique styles for the range, because some styles may be repeated. For old-style TextEdit records, this function always returns 1.
This function is useful for calculating the amount of memory that would be required for a contemplated _TECut or _TECopy. Since the style scrap record is linear in nature, with one element for each style change, you can multiply the result returned by TENumStyles by SizeOf(ScrpSTElement) and add 2 to get the amount of memory that will be needed.
TECustomHook
PROCEDURE TECustomHook(which : TEHook; VAR addr : ProcPtr;
hTE : TEHandle);
This procedure lets applications customize the functions of TextEdit by setting the TextEdit bottleneck routines. The which parameter specifies which bottleneck routine to replace, and is of type TEHook (described below). When TECustomHook returns, the addr parameter contains the address of the previous bottleneck routine specified by which. This is returned so that bottleneck routines can be daisy-chained.
The internally used fields, recalBack and recalLines now form a handle to the list of TextEdit bottleneck routines. Each TERecord has its own set of bottleneck routines to provide for maximum flexibility. The TECustomHook procedure should always be used to change the bottleneck routines instead of modifying the edit record directly.
Also, it is important to note that you should not clone a TERec. Doing so would duplicate the handle stored in recalBack and recalLines. When one of the TextEdit records was disposed, the handle stored in the copy would be invalid, and TextEdit would crash.
There are four bottleneck routines, TEEOLHook, TEWidthHook, TEDrawHook, and TEHitTestHook, described individually below. When replacing these routines, note that all registers except those specified as containing return values must be preserved. Registers A3 and A4 contain a pointer and a handle to the TextEdit record respectively. Line start positions can be obtained from the lineStarts array in the edit record.
None of these bottleneck routines are called from _TextBox.
TEEOLHook
This routine tests a given character and returns with the appropriate status flags set in the status register. The default action is to merely compare the character with $0D (a carriage return) and return.
On entry: D0 character to compare (byte)
A3 pointer to the TextEdit record (long)
A4 handle to the TextEdit record (long)
On exit: z flag clear if end-of-line character, set otherwise
TEWidthHook
This routine is called any time the width of various components of a line are calculated. The appropriate font, face, and size characteristics have already been set into the current port by the time this routine is called. The default action is to call _Char2Pixel and return.
On entry: D0 length of text to measure (word)
D1 offset into text (word)
A0 pointer to text to measure (long)
A3 pointer to the TextEdit record (long)
A4 handle to the TextEdit record (long)
On exit: D1 width of measured text (word)
TEDrawHook
This routine is called any time the various components of a line are drawn. The appropriate font, face, and size characteristics have already been set into the current port by the time this routine is called. The default action is to call _DrawText and return.
On entry: D0 offset into text (word)
D1 length of text to draw (word)
A0 pointer to text to draw (long)
A3 pointer to the TextEdit record (long)
A4 handle to the TextEdit record (long)
TEHitTestHook
This routine is called to determine the character position in a line given the horizontal offset, in pixels, from the beginning of a line. The default action is to call _Pixel2Char and return. For more information, see the description of _Pixel2Char in the Script Manager chapter of Inside Macintosh, Volume 5 and the Inside Macintosh Interim Chapter Draft, Script Manager 2.0.
On entry: D0 length of text to hit test (word)
D1 pixel offset from start of text (word)
A0 pointer to start of text (long)
A3 pointer to the TextEdit record (long)
A4 handle to the TextEdit record (long)
On exit: D0 pixel width to last offset (low word)
Boolean = TRUE if a character (high word)
offset corresponding to the pixel width was found.
D1 character offset (word)
D2 Boolean = TRUE if the pixel (word)
offset falls within the left side of the character.
TextEdit Data Structures
The illustration on the following page is a graphic representation of the TextEdit data structures. You should use this information only for debugging and so you understand what is going on. For reading or writing these data structures, the TextEdit routines should be used. This will help ensure future compatibility.
Further Reference:
• M.OV.GestaltSysenvirons
TEScroll Bug
Text M.TE.TEScrollBug
Revised by: March 1988
Written by: Bryan Stearns April 1986
A bug in TextEdit causes the following problem: a call to TEScroll with no horizontal or vertical displacement (that is, both dh and dv set to zero) results in disappearance of the insertion point. Since such calls do nothing, they should be avoided:
IF (dh <> 0) OR (dv <> 0) THEN TEScroll(dh,dv,myTEHandle);
Further Reference:
• TextEdit
• M.TE.TextEditBugs
TextEdit: Advice & Descent
Text M.TE.TextEditAdvice
Revised by: March 1988
Written by: Rick Blair June 1986
This technical note will point out some bugs (and possible workarounds), and other items of interest for the TextEdit programmer.
TESelRect
Multiple line selections are often more complex shapes than simple rectangles. If this is the case, the teSelRect field of the TERec is set to the last (bottommost) rectangle in the selection. The teHiHook is called to invert each line of the selection.
The ROM limits the selection range (i.e. the lines that get set into teSelRect) to only those lines which will fit into the viewRect. This means that teSelRect will be left at the last visible line. (The old 64K ROMs made all the calls for the complete selection and just let clipping take care of the rest.)
TEDoText
The parameters of this special hook into TextEdit need a little additional explanation. D3 and D4 are described on page 391 of Inside Macintosh Volume I as being the first and last characters to be redrawn. This is true but specific to the –1 “DoDraw” case. In fact, all the calls to TEDoText are interested in these first and last character positions. They determine the selection for a (1) highlight call, the caret position for a (–2) DoCaret call (where D4 is ignored as it’s assumed to equal D3), etc.
Note that the DoCaret (–2) call behaves differently than described in Inside Macintosh, as well. Good old page 391 says it sets up the pen position for caret drawing. Since an InvertRect call is used to draw the caret if you use the default teCarHook, the ROMs just set up teSelRect, they don’t bother with the QuickDraw pen.
TEScrpLength
Inside Macintosh describes TEScrpLength as a long integer; indeed, four bytes are reserved for this value with the intent of someday using that range of values. However, the ROMs use word operations in their accesses to TEScrpLength and make word calculations with it. This means that the high word of TEScrpLength is used for calculations. This is something to watch out for.
CharWidth
Inside Macintosh says that CharWidth takes stylistic variations into account when determining the width of a character. In fact, for italic and outlined styles the extra width is not taken into account. TextEdit relies on CharWidth for positioning of the caret, etc. If you have chosen to use, for instance, italic style in your TE record you will find that as you type the caret actually overlaps the character to the left and so when the caret is erased some of that character will get erased, too. This is somewhat disconcerting to the user but the program will still function correctly.
Clikloops
If you add your own click loop and try to do something like update scroll bars you may run into trouble. Before your routine gets called, TextEdit will have set clipping down to just the viewRect. You will have to save away the old clipping region, set it out to sufficient size (–32767, –32767, 32767, 32767 is probably OK), do your drawing, then restore TextEdit’s clipping area so that it can function properly.
Further Reference:
• TextEdit
• M.TE.TEScrollBug
• M.TE.EOLAmbiguity
• M.TE.TextEditBugs
TextEdit Bugs in System 4.2
Text M.TE.TextEditBugs
Revised by: March 1988
Written by: Chris Derossi June 1987
This note formerly described the known bugs with the version of Styled TextEdit that was provided with System 4.1. Many of these bugs were fixed in System 4.2. This updated Technical Note describes the remaining known problems.
TEStylInsert
Calling TEStylInsert while the TextEdit record is deactivated causes unpredictable results, so make sure to only call TEStylInsert when the TextEdit record is active.
TESetStyle
When using the doFace mode with TESetStyle, the style that you pass as a parameter is ORed into the style of the currently selected text. If you pass the empty set (no styles) though, TESetStyle is supposed to remove all styles from the selected text. But TESetStyle checks an entire word instead of just the high-order byte of the tsFace field. The style information is contained completely in the high-order byte, and the low-order byte may contain garbage.
If the low-order byte isn’t zero, TESetStyle thinks that the tsFace field isn’t empty, so it goes ahead and ORs it with the selected text’s style. Since the actual style portion of the tsFace field is zero, no change occurs with the text. If you want to have TESetStyle remove all styles from the text, you can explicitly set the tsFace field to zero like this:
VAR
myStyle : TextStyle;
anIntPtr : ^Integer;
BEGIN
...
anIntPtr := @myStyle.tsFace;
anIntPtr^ := 0;
TESetStyle(doFace, myStyle, TRUE, textH);
...
END;
TEStylNew
The line heights array does not get initialized when TEStylNew is called. Because of this, the caret is initially drawn in a random height. This is easily solved by calling TECalText immediately after calling TEStylNew. Extra calls to TECalText don’t hurt anything anyway, so this will be compatible with future Systems.
An extra character run is placed at the beginning of the text which corresponds to the font, size, and style which were in the grafPort when TEStylNew was called. This can cause the line height for the first line to be too large. To avoid this, call TextSize with the desired text size before calling TEStylNew. If the text’s style information cannot be determined in advance, then call TextSize with a small value (like 9) before calling TEStylNew.
TEScroll
The bug documented in M.TE.TEScrollBug remains in the new TextEdit. TEScroll called with zero for both vertical and horizontal displacements causes the insertion point to disappear. The workaround is the same as before; check to make sure that dV and dH are not both zero before calling TEScroll.
Growing TextEdit Record
TextEdit is supposed to dynamically grow and shrink the LineStarts array in the TERec so that it has one entry per line. Instead, when lines are added, TextEdit expands the array without first checking to see if it’s already big enough. In addition, TextEdit never reduces the size of this array.
Because of this, the longer a particular TextEdit record is used, the larger it will get. This can be particularly nasty in programs that use a single TERec for many operations during the program’s execution.
Restoring Saved TextEdit Records
Applications have used a technique for saving and restoring styled text which involves saving the contents of all of the TextEdit record handles. When restoring, TEStylNew is called and the TextEdit record’s handles are disposed. The saved handles are then loaded and put into the TextEdit record. This technique should not be used for the nullStyle handle in the style record.
Instead, when TEStylNew is called, the nullStyle handle from the style record should be copied into the saved style record. This will ensure that the fields in the null-style record point to valid data.
TextEdit Conversion Utility
Text M.TE.TextEditConvert
Revised by: March 1988
Written by: Harvey Alcabes April 1985
Text sometimes must be converted between a Pascal string and “pure” text in a handle. This note illustrates a way to do this using MPW Pascal.
Text contained in TextEdit records sometimes must be passed to routines which expect a Pascal string of type Str255 (a length byte followed by up to 255 characters). The following MPW Pascal unit can be used to convert between TextEdit records and Pascal strings:
UNIT TEConvert;
{General utilities for conversion between TextEdit and strings}
INTERFACE
USES MemTypes,QuickDraw,OSIntf,ToolIntf;
PROCEDURE TERecToStr(hTE: TEHandle; VAR str: Str255);
{TERecToStr converts the TextEdit record hTE to the string str.}
{If necessary, the text will be truncated to 255 characters.}
PROCEDURE StrToTERec(str: Str255; hTE: TEHandle);
{StrToTERec converts the string str to the TextEdit record hTE. }
IMPLEMENTATION
PROCEDURE TERecToStr(hTE: TEHandle; VAR str: Str255);
• Macintosh Memory Management: An Introduction TextEdit
TextEdit EOL Ambiguity
Text M.TE.EOLAmbiguity
Revised by: March 1988
Written by: Rick Blair May 1987
TESetSelect may be used to position the insertion point at the end of a line. There is an ambiguity, though; should the insertion point appear at the end of the preceding line or the start of the following one? It is possible to determine what will happen, as you are about to see.
There is an internal flag used by TextEdit to determine where the insertion point at the end of a line appears. This flag is part of the clikStuff field in the TERec. It is there mainly for the use of TEClick, but it is also used by TESetSelect (although it defaults to the right side of the previous line).
The following code can be used to force the insertion point to appear at the left of the following line when it is positioned at the end of a line; in MPW Pascal:
This Technical Note describes another limit on the length of a TextEdit record that was previously undocumented.
The TextEdit chapters in Inside Macintosh document the 32K character limit on a TextEdit record length. They do not, however, discuss the more subtle constraint on the size of the destRect. By definition, the destRect uses integer values for the top-left and bottom-right boundary points. It is possible to have values too large for the destRect without reaching the teLength limit.
The nLines field gives the number of lines in the edit record, and the lineHeight field specifies the vertical distance from the ascent line from one line of text to the ascent line of the next line. In styled TextEdit, the lineHeight may vary for each line depending on the font and font size. These values are entries in the lineHeight table.
Figure 1–LineHeight
The product of the lineHeight (or the largest lineHeight value in styled TextEdit) and nLines gives a good approximation, in pixels, of the vertical dimension of the destRect of the TERec used. If this value is greater than 32,768, then unpredictable and erratic behavior may result.
For example:
2,400 lines of Chicago 12 point yields nLines = 2,400 and lineHeight = 16.
nLines * lineHeight = 2400 * 16 = 38,400. This is above the 32K limit.
1,200 lines of Times 24 point yields nLines = 1,200 and lineHeight = 30.
nLines * lineHeight = 1200 * 30 = 36,000. This is above the 32K limit.
In both of the examples above, the number of characters in the edit record was less than 32,768.
Check All Constraints
Both TELength and the size of the destRect must be under the 32K limit. You can compute an approximate vertical height of the destRect by finding the product of nLines and lineHeight (or the largest lineHeight value in styled TextEdit).
Revised by: Developer Support Center December 1992
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As this month:
Extending TextEdit beyond 32K limit
Controlling TEIdle’s insertion point color
Extending TextEdit beyond 32K limit
Date written: 9/16/92
Last reviewed: 11/1/92
How do I get around the 32K limit in TextEdit? For example, if I have a 64K text file and I want to read it in and display it in a window, I can only read the first 32K. How do I make the text display smoothly when I need to read the next block of text in?
___
Unfortunately, there’s no supported way of doing this. In the early days, TextEdit was designed for use primarily with the Dialog Manager, for displaying and editing small amounts of text at a time. Since then, many developers have used it, or wanted to use it for much more than it was intended. See the Technical Note “Don’t Abuse the Managers,” available on the Developer’s CD, (path: “Dev.CD Sept 92:Technical Documentation:Macintosh Technical Notes:Overview:Managerial Abuse”).
The Word Solution Engine by DataPak is a replacement for TextEdit that allows you to go beyond the 32K limit, as well as including many other features. Other packages are available as well. Check the Redgate Macintosh Directory on AppleLink or publications such as MacTutor magazine to find them.
Controlling TEIdle’s insertion point color
Date written: 7/6/92
Last reviewed: 11/1/92
Is it possible to control the color of the insertion point when I use TEIdle? If I have a light blue background color, the caret is always the invert of that background color and doesn’t have enough contrast to see it. I want to make the color of the caret black if possible.
___
As is often the case, some of the seemingly simplest questions tend to yield the most interesting answers. Succinctly, your question stabs at the wobbly knees of QuickDraw’s color model. Sample code to change the color of the caret in a TextEdit record is in the Snippets folder. Here’s how to implement the color caret.
You can use the caretHook field of the TextEdit record (IM I-379). Basically, this procedure is called repeatedly by TEIdle to draw the caret. The function calls InvertRect each time. It draws the caret, and then it erases the caret. Unfortunately, this function needs to be implemented in assembly. So there are a few gotchas:
• The pointer to the rectangle for the caret is passed on the stack (not the whole rectangle).
• The function InvertRect becomes _InverRect in assembly.
• You can only use A4 for scratch (A0 and A1 are blown away by PaintRect), and then only if you saved and restore it.
• Make sure you pop off the rectangle pointer if you do not use it.
The default system function is simply _InverRect because the rectangle pointer is on the stack and _InverRect pops it off. For color, inverting is not the best idea, as you have noted. Inversion, if it isn’t black and white, does not give you the most contrast. Thus, we need another idea.
But, remember, we have a requirement to paint once, and then erase on the next pass. Inversion works nicely this way. To meet this requirement, use Xor mode with PaintRect. As you know, Xor mode is “or” mode with the double 1s being set to 0 instead of 1. This trait is ideal in your situation because, always having the same caret pattern, drawing a second time obliterates the previous drawing of the caret. Thus, we get the blinking behavior. And, with the Macintosh II and Color QuickDraw, we get these cool things called pixel patterns (IM V-55). You can use your own pixel pattern as the pen pattern, bring it in from the resource, and then use PaintRect in the patXor mode to achieve the desired effect. Done, right?
Unfortunately, here we run into a major flaw of QuickDraw: It is based on two color models, indexed and direct. And, arithmetic operations are performed on the pixel values, and not the colors. In the indexed model, a pixel value of 0x00 is white. (The pixel value in the indexed color model is really an index into a color table.) If you Xor it with 0xFF, you’ll get 0xFF, or black. If you Xor 0xFF with 0xFF, you’ll get 0x00 or white again. On the other hand (in the direct color model), a pixel value of 0x00 is black. If you Xor it with 0xFF, you’ll get 0xFF, or white. This is a problem.
So, you’ll need to perform conditional caret coloring (sounds like a disease). Depending on the depth, you’ll need to use a different pixel pattern. One good way is to use DeviceLoop (Inside Macintosh Volume VI, page 21-23). Though it’s included with System 7.0, there’s a simulated one as described in the article “Multiple Screens Revealed” on page 57 of develop #10. DeviceLoop lets you provide a routine that is called for each screen that the drawing area touches. In this case, the drawing routine is your PaintRect routine for the caret. This drawing routine gets passed the depth of the screen. So, based on the depth of the screen, you can use a different pixel pattern to draw with. For pixel depth of 32 or direct color mode, you can use one pattern, and for the rest you can use a different pattern.
Note, the drawing routine used by DeviceLoop allows you to pass in a LONGINT of userdata. With this longint, you can pass in the pointer to the rectangle from the assembly routine.
Resizing a Macintosh edit field for TEPaste
Written: 11/27/91
Last reviewed: 11/27/91
I have a small edit field and when I call TEPaste, the edit field contents scroll up. How can I calculate what the height of the edit field would be after the paste, resize the edit field to be that size, and then do the paste?
___
There are a couple of ways to implement this:
Method #1:
1. Set the clip region to something other than the viewRect of the edit field.
2. Call TEPaste or TEStylPaste to insert the clipboard text.
3. Call TEGetHeight to get the new height of the text.
4. Set the viewRect of the TERec to the new height. Call TECalText for good measure (and good lineStarts).
5. Finally, set the clip region back to normal and call TEUpdate.
Method #2:
1. Call TENew or TEStylNew to allocate a brand spankin’ new temporary TERec, and give it a viewRect and destRect that’s off the screen but that has the same width as the original TERec.
2. Next, copy the hText handle of the window’s TERec into the hText field of the temporary TERec (good job for BlockMove [IM II-44]).
3. Call TECalText and TEActivate on it; then paste the clipboard text into the temporary TERec. Since the view and dest Rects are off the screen, none of this will be seen by the user.
4. Call TEGetHeight to get the new height, then copy the text back into the original TERec.
5. Finally, call TECalText, then TEUpdate and TEActivate on the original TERec.
Method #1 has less overhead and is a little easier to implement. However, if you have any custom hooks installed in your TERec, then you probably want to let TextEdit do as much of the work as possible. In this case, method #2 is a better way.
Saving and restoring styled Macintosh TextEdit records
Written: 11/26/90
Last reviewed: 12/19/90
How do I save and restore Macintosh records created with TextEdit that contain styles? Where can I find additional information or sample code?
___
There’s no standard for saving text with styles created by TextEdit, so all we can offer is a recommendation that’s modeled after the way that TECopy works on styled text. In styled TextEdit, the style information is stored separately from the text rather than embedded within it. TECopy maintains this separation when styled text is copied, so our recommendation maintains this separation, too.
To save the text part, you could make a file of type TEXT and save the text in the data fork of the file, like TeachText does. You could also save it in a resource of type TEXT.
The best way to save the style information is to use the StScrpRec record. Other records used by styled TextEdit contain references to other structures or they contain only a part of the style information, and that makes saving them awkward. The StScrpRec doesn’t contain references to any other structures, and it contains enough information to rebuild the entire tree of TextEdit structures. When you call TECopy on styled text, both a 'TEXT' and 'styl' scrap type is placed into the desk scrap. The 'TEXT' type just contains the text. The 'styl' type contains a StScrpRec containing all of the style information for the copied text. TextEdit doesn’t maintain a StScrpRec for the entire text buffer, so you have to tell TextEdit to do this for you when you’re ready to save the style information.
Fortunately, one call does it all. Inside Macintosh Volume V, page 268, documents the GetStylScrap routine. It creates a new StScrpRec record, places all the style information of the selected text into it, and returns a handle to this StScrpRec. First, select the text you want to save either by calling TESetSelect or by setting the selStart and selEnd fields of the TextEdit record if you don’t want the selection to be visible to the user. (If you do this, remember to restore the old values of selStart and selEnd after you get the StScrpHandle.) Next, call GetStylScrap to construct the StScrpRec. You can now save the resulting StScrpRec, perhaps in a resource of type 'styl' or in whatever way you see fit.
Reading the text and styles back into a TextEdit record isn’t much more complicated. First, read the text back into a buffer and read the 'styl' resource (or however you saved it) into a StScrpRec. Create a styled TextEdit record (or use an old one), then call TEStylInsert, passing it a pointer to the text buffer and a handle to the StScrpRec. Your TextEdit record should then contain and display your original styled text. That’s all!
Macintosh SetClikLoop procedure and TERec clikLoop field
Written: 7/30/91
Last reviewed: 8/1/91
I have had some problems with the SetClikLoop procedure that don’t appear when I set the clikLoop field directly (in C). Why does TextEdit’s SetClikLoop procedure create a special procedure to call my clikLoop routine, rather that just installing the clikLoop directly? Inside Macintosh says you can set the clikLoop field directly in assembly. Why not in C?
___
The short answer is that the special procedure is a glue routine for shuttling a result between the stack and the register D0. The larger question is why does installing clikLoop directly work? C, I think, is the culprit.
But, before I get ahead of myself, let me explain the SetClikLoop procedure. As you know, the click loop routine has no parameters but it does return a Boolean result (IM I-380).
FUNCTION MyClikLoop : Boolean; { Pascal declaration }
pascal Boolean MyClikLoop(); { C declaration }
Now, MyClikLoop is called by the TextEdit Manager from inside the TEClick routine. The way the code was written, TEClick expects to get the Boolean result from D0. For assembly programmers, this is no problem. In Pascal, the return value is placed on the stack. So, D0 does not contain your routines’ Boolean and what TEClick grabs will be “who knows what” and your program should crash. SetClikLoop will install a glue routine that calls MyClikLoop. The glue routine will move the result from the stack to D0. By the way, this information is documented on pages 237–238 of Volume Two of Macintosh Revealed, Second Edition, by Stephen Chernicoff.
Now why is your program causing problems when you use SetClikLoop? You might have declared your MyClikLoop as a C function. Since MyClikLoop takes no parameters, nothing is put on the stack. But C, unlike Pascal, places its return value in D0. So, for C, the glue routine is not needed. On the other hand, if you use the SetClikLoop routine with a C routine, you will take the top 2 bytes off the stack and clobber D0 with the wrong answer. Thus, you might get problems.
Though the C routine may work by setting it directly to the clikLoop field, doing it that way is not the supported method. You should declare your MyClikLoop function as a Pascal function and use SetClikLoop.
Special handling for Control-P keyboard input in a Mac dialog
Written: 6/5/91
Last reviewed: 8/1/91
Under System 6, when Control-P is pressed, a DLE character (hex 10) appears in a Macintosh dialog’s editText field. Under System 7, pressing Control-P produces no change in the editText box. The character is fed in via a normal (unaltered) keyDown event fed to DialogSelect. Does Control-P have some special meaning to DialogSelect or TextEdit now?
___
Believe it or not, and it took several of us scratching our heads to figure this out; yes, it does, and this is by design! What you are running into is the Dialog Manager and TextEdit working together to make Cut, Copy, and Paste function keys work on an extended keyboard. The function keys on the top of an extended keyboard return $10 as their ASCII keycode. Thus, when TEKey detects that it is being called from dialog select, and the key you pass is a $10, it then looks for the event record on the stack and gets the raw keycode to determine which edit operation to perform. The bottom line is that it will not then enter the $10 in the edit field.
There is a simple workaround, and that is to install a filter proc in your dialog that detects keystrokes (actually it could always just look for this certain keystroke) and passes them to TEKey itself, thus skirting the whole issue.
Dimming and disabling a specific TEditText object
Written: 8/8/91
Last reviewed: 8/13/91
How can I dim and disable a specific TEditText object in my TDialogView so the field is not editable?
___
The trick is to use the TView methods Dimstate and ViewEnable to disable and dim the TEditText Views, and then do a TEditText.StopEdit (which disables editing in the TEditText box), and in the other case enable dimming and the view itself and use the TEditText.StartEdit (which enables editing in the TEditText box), as shown in the C++ example below:
MacApp sample code using this technique is included with DTS’s Snippets files.
Get TEHandle from DialogPeek, not GetDItem
Written: 1/17/92
Last reviewed: 2/17/92
I want to show all characters in my Macintosh control panel as bullets where the password is typed in. I cannot get the TECustomHook to work properly, it crashes before it gets to my hook routine. In the following Think C example, CPtr is dereferenced from the handle used in the control panel’s cdevValue field:
GetDItem does NOT return a TEHandle; it returns a handle to the current text of the edit item. You must get TEHandle from DialogPeek and must always monitor and change the TECustomHook on the fly. There is only one TEHandle for a dialog, no matter how many edit items are associated with the dialog. See the description of the DialogRecord data type in Inside Macintosh.
Cursor flicker while using Macintosh TextEditWritten: 5/6/92
Last reviewed: 9/15/92
The cursor flashes when the user types in TextEdit fields in my Macintosh application. This is done in TEKey. I notice that most programs hide the cursor once a key is pressed. I don’t care for this because then I have to move the mouse to see where I am. Is this a typical fix for this problem and an accepted practice?
___
There’s very little you can do to avoid this. The problem is that every time you draw anything to the screen, if the cursor’s position intersects the rectangle of the drawing being done, QuickDraw hides the cursor while it does the drawing, and then shows it again to keep it from affecting the image being drawn beneath it. Every time you enter a character in TextEdit, the nearby characters are redrawn. Usually this is invisible because the characters just line up on top of their old images, but if the cursor is nearby and visible, it will flicker while it’s hidden to draw the text. This is why virtually all programs call ObscureCursor when the user types. Also, most users don’t want the image of the cursor obscuring text they might be referring to, yet they don’t want to have to move it away and then move it back to make selections. Because it’s so commonplace, hiding the cursor probably won’t bother your users; in fact, they might very well prefer the cursor hidden. This, combined with the fact that there’s very little you can do to help the flickering, suggests that you should obscure the cursor while the user types.
TextEdit Technicalities
Text M.TE.TextEditTech
Revised by: Mary Burke April 1990
Written by: Mary Burke February 1990
This Technical Note discusses some areas in TextEdit that have not previously been clearly documented.
Changes since February 1990: Added a note about the changes in TextEdit for System Software 6.0.5, documented the low-memory global TESysJust, clarified information about text direction and _TESetJust, discussed problems with the SetWordBreak routine along with a solution to work around it, and described the differences in dialog text item behavior.
TextEdit in 6.0.5
In addition to all the features of earlier versions, TextEdit 3.0 now allows you to take advantage of the Script Manager’s handling of systems with more than one script system installed. TextEdit uses the Script Manager to support such systems and now exhibits the correct behavior for editing and displaying text in multiple styles and different scripts. Multiple scripts can even exist on a single line due to TextEdit’s use of the Script Manager. The new version of TextEdit in 6.0.5:
• handles mixed-directional text
• synchronizes keyboards and fonts
• handles double-byte characters
• determines word boundaries and line breaks
• provides outline highlighting in the background
• buffers text for performance improvements
• permits left justification in right-to-left directional scripts
• customizes word breaking
• customizes measuring
Refer to the TextEdit chapter in Inside Macintosh, Volume VI, for detailed documentation on TextEdit 3.0.
The LineStarts Array and nLines
The LineStarts array is a field in a TextEdit record that contains the offset position of the first character of each line. This array has the following boundary conditions:
• It is a zero-based array.
• The last entry in the array must have the same value as teLength.
• The maximum number of entries is 16,000.
To determine the length of a line you can use the information contained in the lineStarts array and nLines. For example, if you want to determine the length of line n, subtract the value contained in entry n of the array from the value in the entry (n+1):
The terminating condition for this measurement is when n = nLines + 1. It is important not to change the information contained in the array.
TESysJust
TESysJust is a low-memory global that specifies the system justification. The default value of this global is normally based on the system script. It is -1 when a system’s default line direction is right to left, and 0 for a default left-to-right line direction. Applications may change the value using the Script Manager routine SetSysJust; however, these applications should save the current value before using it and restore it before exiting the application or processing a MultiFinder suspend event. The current value may be obtained using the Script Manager routine GetSysJust.
Forcing Text Direction
The original TextEdit documentation introduced _TESetJust with three possible choices for justification: teJustLeft (0), teJustCenter (1), and teJustRight (-1). These choices are appropriate for script systems that are read from left to right. However, in script systems that are read from right to left, text is incorrectly displayed as left justified in dialog boxes and in other areas of applications where users cannot explicitly set the justification. To fix this problem, the behavior of teJustLeft has changed to match the line direction of the system in use, which is the value stored in TESysJust. Another constant has been added to allow an application to force left justification: teForceLeft (-2). This constant has been available for some time, but it has not been documented until now. If your application does not allow the user to change the justification, then it should use teJustLeft; if it does, then it should use teForceLeft for left justification.
A Little More on Redraw in _TESetStyle
If the redraw parameter used in _TESetStyle is FALSE, line breaks, line heights, and line ascents are not recalculated. Therefore a succeeding call to a routine using any of this information does not reflect the new style information. For example, a call to _TEGetHeight (which returns a total height between two specified lines) uses the line height set previous to the _TESetStyle call. A call to _TECalText is necessary to update this information. If redraw is TRUE, the current style information is reflected. This behavior also holds for the redraw parameter in _TEReplaceStyle.
TEDispatchRec
There is currently space reserved for four documented hooks in the TEDispatchRec: TEEolHook, TEWidthHook, TEDrawHook and TEHitTestHook. The space beyond these hooks is reserved, and any attempt to use this private area results in corrupted TextEdit data.
Custom Word Breaks
A problem exists in one of TextEdit’s advanced procedures, SetWordBreak. The current glue code does not preserve the state of the registers correctly; however, the solution is fairly simple. Instead of calling SetWordBreak and passing a pointer to your custom word break routine, pass the pointer to your external glue which should call your custom word break routine. Following is the glue code that correctly handles the registers:
WordBreakProc PROC EXPORT
IMPORT MYWORDBREAK ;Must be uppercase here
MOVEM.L D1-D2/A1,-(SP)
CLR.W -(SP) ;Space for result
MOVE.L A0,-(SP) ;Move the ptr to stack
MOVE.W D0,-(SP) ;Move the charpos to Stack
JSR MYWORDBREAK
MOVE.W (SP)+,D0 ;Set Z bit
MOVEM.L (SP)+,D1-D2/A1
RTS
ENDP
An external declaration is also necessary:
FUNCTION WordBreakProc( text: Ptr; charPos: INTEGER ) : BOOLEAN; EXTERNAL;
as is the function itself. One thing that should be noted is that it is not really necessary to have MyWordBreak boolean, but rather to have the Z bit set properly. The result of the function should be zero when you do not want a break; otherwise, a non-zero value indicates a break is desired.
FUNCTION MyWordBreak( text : Ptr; charPos : INTEGER ) : INTEGER;
{ Your word break code here. }
For more information, refer to the TextEdit chapter of Inside Macintosh, Volume I-380.
Static and Editable Text
The Dialog Manager depends on TextEdit to display text in dialog boxes. For an editable text field, the Dialog Manager simply calls _TEUpdate. Before making this call, it may double the width of the rectangle to contain the text if the height of the rectangle is sufficient for only one line and the line direction specified by TESysJust is left to right. In this case, the Dialog Manager extends the rectangle on the right. Note, however, this does not occur when your line direction is right to left.
For static text items, _TextBox is used instead. When the display rectangle is not large enough. _TextBox clips the text to the size of the specified rectangle. To avoid the clipping problem, simply make the display rectangle larger. If your dialog box contains both static and editable text items, the difference in the text handling may appear inconsistent.
Further Reference:
• Inside Macintosh, Volumes I,V & VI, TextEdit
• Inside Macintosh, Volume V, The Script Manager
• Inside Macintosh, Volume I, The Dialog Manager
• M.TE.TestEditChanges
TrueType Q&As
Imaging M.QD.TrueType.Q&As
Revised by: Developer Support Center October 1992
Written by: Developer Support Center October 1990
This Technical Note contains a collection of Q&As relating to a specific topic—questions you’ve sent the Developer Support Center (DSC) along with answers from the DSC engineers. While DSC engineers have checked the Q&A content for accuracy, the Q&A Technical Notes don’t have the editing and organization of other Technical Notes. The Q&A function is to get new technical information and updates to you quickly, saving the polish for when the information migrates into reference manuals.
Q&As are now included with Technical Notes to make access to technical updates easier for you. If you have comments or suggestions about Q&A content or distribution, please let us know by sending an AppleLink to DEVFEEDBACK. Apple Partners may send technical questions about Q&A content to DEVSUPPORT for resolution.
New Q&As and Q&As revised this month are marked with a bar in the side margin.
SetPreserveGlyph and font glyph preservation
Written: 7/22/91
Last reviewed: 8/1/92
TrueType’s SetPreserveGlyph call with preserveGlyph set to “true” works as you’d expect for Times and Helvetica, but it has no effect on New York, Geneva, Monaco, or Chicago. Why does SetPreserveGlyph work this way?
___
Glyph preservation is a function of the font. It turns out that Times and Helvetica have glyphs that extend above the ascent line, thereby enabling SetPreserveGlyph to have an effect on a particular glyph. New York, Geneva, Monaco, and Chicago’s characters all fit between the ascent and descent lines, and so do not need to be compressed to fit if preserveGlyph is true. A font must have glyphs extending above or below the ascent or descent lines for SetPreserveGlyph to have an effect.
RealFont and TrueType
Written: 9/4/91
Last reviewed: 8/1/92
What is the story with RealFont and TrueType? I am finding that, of the standard System 7 fonts, only Symbol and Courier get a TRUE for 7 point.
___
You are absolutely correct with your observation of RealFont for size 7 and certain TrueType fonts. The explanation is hidden in “The TrueType Font Format Specification” (APDA M0825LL/A), page 227:
The “head” FontHeader table contains a field “lowestRecPPEM,” which indicates the “lowest recommended pixel number per Em,” or, in other words, the “smallest readable size in pixels.” As it turns out, the Font Manager in its wisdom uses this information for the value it returns from RealFont. Note that for higher resolution devices, a point size of 7 does *not* correspond to 7 pixels; but as the unit “point” is 1/72 inch, and the screen resolution is (approximately) 72 dpi, the result corresponds to reality in this case.
The value for lowestRecPPEM can be arbitrarily set by the Font designer. We all know that small point sizes on low-resolution devices never look “great,” and even less so for outline fonts. Courier and Symbol have lowestRecPPEM = 6, while the other outline fonts in the System have lowestRecPPEM = 9. This doesn’t really mean that Courier and Symbol in size 7 (TrueType) look better than Times or Helvetica under the same conditions. It means the font designer had higher standards (or was in a different mood) when he choose lowestRecPPEM = 9.
Where to find OutlineAccessLib
Written: 12/9/91
Last reviewed: 8/1/92
Is the OutlineAccessLib library, which will provide standardized access routines to TrueType font outline bezier data, available yet? I have been unable to find this in the last few Developer CDs or the MPW 3.2 C release CD.
___
The OutlineAccessLib is now available (although somewhat disguised and hidden) on the Developer CD Vol. X (and later); it is included in the code accompanying the article “Curves ahead” in issue #8 of develop.
SetOutlinePreferred affects only calling application
Written: 3/9/92
Last reviewed: 8/1/92
Is the property that is managed by SetOutlinePreferred and GetOutlinePreferred kept on an application-by-application basis? In other words, will calling SetOutlinePreferred affect only my application? As the current value of outlinePreferred is saved in a PICT, will playing a PICT affect the playing application’s current outlinePreferred setting? I assume that outlinePreferred is not an attribute of a GrafPort; is this true?
___
The outlinePreferred setting is stored as a low-memory global value for your application. It is saved and restored during context switches, so it only effects your application and no one else’s.
DrawPicture should not alter the state of the global for you. While DrawPicture internally may set or reset this value, it’s supposed to put it back the way it found it when it is done. So, playing a PICT will not affect the current application’s outline settings.
And no, OutlinePreferred is not part of a grafport (as mentioned above.)
QuickDraw doesn’t draw ASCII 32 ($20) character
Written: 3/18/92
Last reviewed: 8/1/92
My TrueType font has all 256 characters defined with a unique glyph. I have been unable to draw the $20 (space) character. DrawChar, DrawText, DrawString and DrawJust all ignore this character in the font and draw a blank character. How can I draw it?
___
Unfortunately, the problem with the space character not being drawn is hard-coded into the text-drawing routine in the core of QuickDraw. ASCII 32 is always “optimized away,” regardless of the font being used, regardless of the particular circumstances. The only workaround is to put the corresponding character elsewhere in the ASCII character encoding (or, if this is not possible, to use an additional font).
You are lucky that TrueType fonts always render the ASCII code 13 (carriage return) if it has a glyph in the font; for bitmap fonts, if the character drawing happens with scaling, or with foreground/background colors different from black/white, even the CHR(13) drawing has been optimized away.
How to Construct Word-Break Tables
Text M.TE.WordBreakTables
Revised by: March 1988
Written by: Mark Davis November 1987
This technical note describes how to construct auxiliary break tables for use with the FindWord routine in the Script Manager.
Constructing break tables
The FindWord algorithm finds word boundaries by determining where words should not be broken. For example, “re-do” is one word: it should not be broken at the hyphen. In other words, a sequence of the form: (letter, hyphen, letter) should not be broken between the first and second or second and third character. This is called a continuation sequence. The algorithm used by the FindWord routine allows for continuation sequences of lengths one, two and three. Examples of a sequence of length two include (letter, letter), or (number, number). For a length of one, there is only one sequence, consisting of the characters of type nonBreaking: these characters are never separated from preceding or following characters.
For most scripts, this information about continuation sequences is packed into a table for use by the FindWord algorithm. (For complex scripts like Japanese, a different algorithm is used for portions of the script.) The default break tables for a given script can be overridden by a user-specified breakTable parameter, but should only be used for known scripts. That is, before overriding the breakTable parameter, the programmer should first check the script of the current font.
A break table consists of two sections, a 256 byte character type table followed by a character triple table.
The character type table is indexed by the character’s ASCII code and contains one type value for each character. The character types in the table are limited to values between 1 and 31. There are two distinguishing values: the type nonBreaking (= 1) indicates that the character is non-breaking; it always continues a word. The type wild (=0) indicates that the character may or may not break, depending on information in the character triple table, as described below. Otherwise, the choice of numbers to represent character types is completely arbitrary.
For example, the following in MPW Assembler defines character types for use in a word-selection break table, then sets up a character type table using an assembly macro (setByte) to store character type values in an array. (Note that the character types could have been defined with equate definitions (EQU), rather than using the record structure.) Writing the setByte macro is left as an exercise to the reader. Note that the break value is the default. This value is not distinguished, but should have no continuation sequences.
The character triple table is a coded representation of a list of continuation sequences. It consists of a list of packed one word triples, preceded by a length word. This length word contains the number of triples minus one. Each triple contains three character types, either as derived from the charType table or the special type wild (= zero). The three types in a triple are packed into fields five bits apiece, with the most significant bit in the word cleared. The first type in the triple is the leftmost.
A continuation sequence of length three (xyz) is represented by entering three triples into the triple list: xyz, *xy, and yz* (where ‘*’ stands for the type wild, which is always zero).
A continuation sequence of length two (xy) is represented by entering two triples into this list: *xy, and xy*. A continuation sequence of length one has no entry in the triple list: the character type is simply nonBreaking.
Note that the type wild cannot appear as the middle element of a triple. The words in the triple table must be sorted in ascending numerical order for future compatibility.
The following is an example of how a character triple table could be coded. The defSeq macro takes a continuation sequence as a parameter, and enters a set of triples into an internal array. The dumpSeq macro sorts the triples, and stores them in the proper order with dc.w commands. Once again, writing the macros defSeq and dumpSeq is left as an exercise for the reader.
A series of blanks should generally select as a single word. Make certain, however, that a carriage return does not continue a word to the right (note how it has a separate character type from blank for this reason), otherwise word selection and wrapping do not work properly across paragraphs.
Extensions
The values 16-31 in the character type table entry for null ($00) (the first byte in the character type table) are reserved by Apple for future expansion. The use of one of these values indicates the presence of a supplementary table after the triple table.